1 module served.backend.lazy_workspaced; 2 3 import std.algorithm; 4 import std.experimental.logger; 5 6 import workspaced.api; 7 import workspaced.backend; 8 9 alias LazyLoadHook = void delegate() nothrow; 10 alias LazyLoadHooks = LazyLoadHook[]; 11 12 alias InstanceLoadHook = void delegate(); 13 14 class LazyWorkspaceD : WorkspaceD 15 { 16 static class LazyInstance : WorkspaceD.Instance 17 { 18 private LazyWorkspaceD backend; 19 private LazyLoadHooks[string] lazyLoadCallbacks; 20 private InstanceLoadHook[] accessCallbacks; 21 private bool wasAccessed; 22 ComponentFactory[] lazyComponents; 23 24 bool didCallAccess() const 25 { 26 return wasAccessed; 27 } 28 29 void onLazyLoad(string component, LazyLoadHook hook) 30 { 31 foreach (com; instanceComponents) 32 if (com.info.name == component) 33 return hook(); 34 35 lazyLoadCallbacks.require(component) ~= hook; 36 } 37 38 void onLazyLoadInstance(InstanceLoadHook hook) 39 { 40 if (wasAccessed) 41 return hook(); 42 else 43 accessCallbacks ~= hook; 44 } 45 46 override void onBeforeAccessComponent( 47 ComponentInfo info) const 48 { 49 accessCheck(); 50 51 // lots of const-remove-casts because lazy loading should in theory 52 // not break anything constness related 53 foreach (i, com; lazyComponents) 54 if (com.info.name == info.name) 55 { 56 trace("Lazy loading component ", 57 info.name); 58 59 Exception error; 60 auto wrap = (cast() com).create(cast() backend, 61 cast() this, error); 62 if (wrap) 63 (cast() this).attachComponent( 64 ComponentWrapperInstance(wrap, 65 com.info)); 66 else if (backend.onBindFail) 67 backend.onBindFail(cast() this, 68 cast() com, error); 69 return; 70 } 71 72 super.onBeforeAccessComponent(info); 73 } 74 75 override bool checkHasComponent(ComponentInfo info) const nothrow 76 { 77 try 78 { 79 accessCheck(); 80 } 81 catch (Exception) 82 { 83 return false; 84 } 85 86 foreach (com; lazyComponents) 87 if (com.info.name == info.name) 88 return true; 89 90 return super.checkHasComponent(info); 91 } 92 93 void attachLazy(ComponentFactory factory) 94 { 95 lazyComponents ~= factory; 96 } 97 98 override bool attach(WorkspaceD workspaced, 99 ComponentInfo info) 100 { 101 foreach (factory; workspaced.components) 102 { 103 if (factory.info.name == info.name) 104 { 105 attachLazy(factory); 106 return true; 107 } 108 } 109 throw new Exception("Component not found"); 110 } 111 112 protected override void attachComponent( 113 ComponentWrapperInstance component) 114 { 115 accessCheck(); 116 117 lazyComponents = lazyComponents.remove!( 118 a => a.info.name == component.info.name); 119 instanceComponents ~= component; 120 121 auto hooks = lazyLoadCallbacks.get(component.info.name, null); 122 lazyLoadCallbacks.remove(component.info.name); 123 foreach (hook; hooks) 124 hook(); 125 } 126 127 protected void accessCheck() const 128 { 129 if (!wasAccessed) 130 { 131 cast()wasAccessed = true; 132 try 133 { 134 trace("attaching cwd ", cwd); 135 136 foreach (hook; accessCallbacks) 137 hook(); 138 139 cast()accessCallbacks = null; 140 } 141 catch (Exception e) 142 { 143 error("failed attaching project: ", e); 144 throw e; 145 } 146 } 147 } 148 } 149 150 override void onBeforeAccessGlobalComponent( 151 ComponentInfo info) const 152 { 153 154 } 155 156 override bool checkHasGlobalComponent(ComponentInfo info) const 157 { 158 return super.checkHasGlobalComponent(info); 159 } 160 161 protected override Instance createInstance( 162 string cwd, Configuration config) 163 { 164 auto inst = new LazyInstance(); 165 inst.cwd = cwd; 166 inst.config = config; 167 inst.backend = this; 168 return inst; 169 } 170 171 protected override void autoRegisterComponents( 172 Instance inst) 173 { 174 auto lazyInstance = cast(LazyInstance) inst; 175 if (!lazyInstance) 176 return super.autoRegisterComponents(inst); 177 178 foreach (factory; components) 179 { 180 if (factory.autoRegister) 181 { 182 lazyInstance.attachLazy(factory); 183 } 184 } 185 } 186 187 override void onRegisterComponent( 188 ref ComponentFactory factory, bool autoRegister) 189 { 190 components ~= ComponentFactoryInstance(factory, autoRegister); 191 auto info = factory.info; 192 Exception error; 193 auto glob = factory.create(this, null, error); 194 if (glob) 195 globalComponents ~= ComponentWrapperInstance(glob, info); 196 else if (onBindFail) 197 onBindFail(null, factory, error); 198 199 if (autoRegister) 200 foreach (ref instance; instances) 201 { 202 auto lazyInstance = cast(LazyInstance) instance; 203 if (lazyInstance) 204 lazyInstance.attachLazy(factory); 205 else 206 { 207 auto inst = factory.create(this, 208 instance, error); 209 if (inst) 210 instance.attachComponent(ComponentWrapperInstance(inst, 211 factory.info)); 212 else if (onBindFail) 213 onBindFail(instance, factory, error); 214 } 215 } 216 } 217 218 override bool attach(Instance instance, 219 string component, out Exception error) 220 { 221 auto lazyInstance = cast(LazyInstance) instance; 222 if (!lazyInstance) 223 return super.attach(instance, component, error); 224 225 foreach (factory; components) 226 { 227 if (factory.info.name == component) 228 { 229 lazyInstance.attachLazy(factory); 230 return true; 231 } 232 } 233 return false; 234 } 235 236 bool attachEager(Instance instance, 237 string component, out Exception error) 238 { 239 return super.attach(instance, component, error); 240 } 241 }