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 }