1 /**
2  * An engine for provisioning via
3  * multiple providers
4  *
5  * Authors: Tristan Brice Velloza Kildaire (deavmi)
6  */
7 module hummus.engine;
8 
9 import hummus.provider;
10 
11 version(unittest)
12 {
13     import gogga.mixins;
14     import std.string : format;
15 }
16 
17 // Else it keeps trying to call to the
18 // one in the `Engine` itself
19 import hummus.cfg : fill_outer = fill;
20 
21 /**
22  * The `Engine` is a provider which
23  * allows multiple _other_ providers
24  * to be attached to it.
25  *
26  * During provisioning if a name
27  * is found in a provider then it
28  * is returned, else the next provider
29  * is checked.
30  *
31  * The order in which providers are
32  * attached is important as that
33  * is the order in which they will
34  * be queried
35  */
36 public class Engine : Provider
37 {
38     private Provider[] _ps;
39 
40     /**
41      * Constructs a new engine with
42      * no providers attached
43      */
44     this()
45     {
46 
47     }
48 
49     /**
50      * Attach a provider
51      *
52      * Params:
53      *   p = the provider to attach
54      */
55     public void attach(Provider p)
56     {
57         if (p is null)
58         {
59             // todo: check
60             return;
61         }
62         this._ps ~= p;
63 
64         version(unittest)
65             DEBUG(format("Attached provider '%s'", p));
66     }
67 
68     protected bool provideImpl(string n, ref string v)
69     {
70         foreach (Provider p; this._ps)
71         {
72             auto pr_opt = p.provide(n);
73             if (pr_opt.isPresent())
74             {
75                 v = pr_opt.get();
76                 return true;
77             }
78         }
79         return false;
80     }
81 
82     /**
83      * Given a structure this will fill
84      * it up with values by querying
85      * the attached provider(s)
86      *
87      * Params:
88      *   structInstance = the structure
89      * to provision
90      */
91     public void fill(T)(ref T structInstance)
92     {
93         fill_outer(structInstance, this);
94     }
95 }
96 
97 version (unittest)
98 {
99     private class DP1 : Provider
100     {
101         protected bool provideImpl(string n, ref string v)
102         {
103             if (n == "Key1")
104             {
105                 v = "Value1";
106                 return true;
107             }
108 
109             return false;
110         }
111     }
112 
113     private class DP2 : Provider
114     {
115         protected bool provideImpl(string n, ref string v)
116         {
117             if (n == "Key2")
118             {
119                 v = "Value2";
120                 return true;
121             }
122 
123             return false;
124         }
125     }
126 }
127 
128 /**
129  * Attach two providers to an engine
130  * and then test out querying for
131  * names
132  */
133 unittest
134 {
135     auto e = new Engine();
136 
137     auto opt1 = e.provide("Key1");
138     auto opt2 = e.provide("Key2");
139     assert(opt1.isEmpty());
140     assert(opt1.isEmpty());
141 
142     e.attach(new DP1());
143     opt1 = e.provide("Key1");
144     opt2 = e.provide("Key2");
145     assert(opt1.isPresent());
146     assert(opt1.get() == "Value1");
147     assert(opt2.isEmpty());
148 
149     e.attach(new DP2());
150     opt1 = e.provide("Key1");
151     opt2 = e.provide("Key2");
152     assert(opt1.isPresent());
153     assert(opt1.get() == "Value1");
154     assert(opt2.isPresent());
155     assert(opt2.get() == "Value2");
156 
157     struct F
158     {
159         string Key1;
160         string Key2;
161     }
162 
163     auto f = F();
164     e.fill(f);
165     assert(f.Key1 == "Value1");
166     assert(f.Key2 == "Value2");
167 }