1 /++ 2 Bits and bobs to register plugins to be instantiated on program startup/connect. 3 4 This should really only have to be used internally. 5 6 Example: 7 --- 8 import kameloso.plugins; 9 import kameloso.plugins.common.core; 10 11 final class MyPlugin : IRCPlugin 12 { 13 mixin IRCPluginImpl; 14 } 15 16 mixin ModuleRegistration; 17 --- 18 19 Example: 20 --- 21 import kameloso.plugins; 22 23 IRCPluginState state; 24 // state setup... 25 26 IRCPlugin[] plugins = instantiatePlugins(state); 27 --- 28 29 Copyright: [JR](https://github.com/zorael) 30 License: [Boost Software License 1.0](https://www.boost.org/users/license.html) 31 32 Authors: 33 [JR](https://github.com/zorael) 34 +/ 35 module kameloso.plugins; 36 37 private: 38 39 import kameloso.plugins.common.core : IRCPlugin, IRCPluginState; 40 41 42 // PluginRegistrationEntry 43 /++ 44 An entry in [registeredPlugins] corresponding to a plugin registered to be 45 instantiated on program startup/connect. 46 +/ 47 struct PluginRegistrationEntry 48 { 49 // priority 50 /++ 51 Priority at which to instantiate the plugin. A lower priority makes it 52 get instantiated before other plugins. 53 +/ 54 Priority priority; 55 56 // ctor 57 /++ 58 Function pointer to a "constructor"/builder that instantiates the relevant plugin. 59 +/ 60 IRCPlugin function(IRCPluginState) ctor; 61 62 // this 63 /++ 64 Constructor. 65 66 Params: 67 priority = [kameloso.plugins.Priority|Priority] at which 68 to instantiate the plugin. A lower priority value makes it get 69 instantiated before other plugins. 70 ctor = Function pointer to a "constructor"/builder that instantiates 71 the relevant plugin. 72 +/ 73 this( 74 const Priority priority, 75 typeof(this.ctor) ctor) pure @safe nothrow @nogc 76 { 77 this.priority = priority; 78 this.ctor = ctor; 79 } 80 } 81 82 83 // registeredPlugins 84 /++ 85 Array of registered plugins, represented by [PluginRegistrationEntry]/-ies, 86 to be instantiated on program startup/connect. 87 +/ 88 shared PluginRegistrationEntry[] registeredPlugins; 89 90 91 // module constructor 92 /++ 93 Module constructor that merely reserves space for [registeredPlugins] to grow into. 94 95 Only include this if the compiler is based on 2.095 or later, as the call to 96 [object.reserve|reserve] fails with those prior to that. 97 +/ 98 static if (__VERSION__ >= 2095L) 99 shared static this() 100 { 101 enum initialSize = 64; 102 (cast()registeredPlugins).reserve(initialSize); 103 } 104 105 106 public: 107 108 109 // registerPlugin 110 /++ 111 Registers a plugin to be instantiated on program startup/connect by creating 112 a [PluginRegistrationEntry] and appending it to [registeredPlugins]. 113 114 Params: 115 priority = Priority at which to instantiate the plugin. A lower priority 116 makes it get instantiated before other plugins. 117 ctor = Function pointer to a "constructor"/builder that instantiates 118 the relevant plugin. 119 +/ 120 void registerPlugin( 121 const Priority priority, 122 IRCPlugin function(IRCPluginState) ctor) 123 { 124 registeredPlugins ~= PluginRegistrationEntry( 125 priority, 126 ctor); 127 } 128 129 130 // instantiatePlugins 131 /++ 132 Instantiates all plugins represented by a [PluginRegistrationEntry] in 133 [registeredPlugins]. 134 135 Plugin modules may register their plugin classes by mixing in [PluginRegistration]. 136 137 Params: 138 state = The current plugin state on which to base new plugin instances. 139 140 Returns: 141 An array of instantiated [kameloso.plugins.common.core.IRCPlugin|IRCPlugin]s. 142 +/ 143 auto instantiatePlugins(/*const*/ IRCPluginState state) 144 { 145 import std.algorithm.sorting : sort; 146 147 IRCPlugin[] plugins; 148 plugins.length = registeredPlugins.length; 149 uint i; 150 151 auto sortedPluginRegistrations = registeredPlugins 152 .sort!((a,b) => a.priority.value < b.priority.value); 153 154 foreach (registration; sortedPluginRegistrations) 155 { 156 plugins[i++] = registration.ctor(state); 157 } 158 159 return plugins; 160 } 161 162 163 // PluginRegistration 164 /++ 165 Mixes in a module constructor that registers the supplied [IRCPlugin] subclass 166 to be instantiated on program startup/connect. 167 168 Params: 169 Plugin = Plugin class of module. 170 priority = Priority at which to instantiate the plugin. A lower priority 171 makes it get instantiated before other plugins. Defaults to `0.priority`. 172 module_ = String name of the module. Only used in case an error message is needed. 173 +/ 174 mixin template PluginRegistration( 175 Plugin, 176 Priority priority = 0.priority, 177 string module_ = __MODULE__) 178 { 179 // module constructor 180 /++ 181 Mixed-in module constructor that registers the passed [Plugin] class 182 to be instantiated on program startup. 183 +/ 184 shared static this() 185 { 186 import kameloso.plugins.common.core : IRCPluginState; 187 188 static if (__traits(compiles, new Plugin(IRCPluginState.init))) 189 { 190 import kameloso.plugins : registerPlugin; 191 192 static auto ctor(IRCPluginState state) 193 { 194 return new Plugin(state); 195 } 196 197 registerPlugin(priority, &ctor); 198 } 199 else 200 { 201 import std.format : format; 202 203 enum pattern = "`%s.%s` constructor does not compile"; 204 enum message = pattern.format(module_, Plugin.stringof); 205 static assert(0, message); 206 } 207 } 208 } 209 210 211 // Priority 212 /++ 213 Embodies the notion of a priority at which a plugin should be instantiated, 214 and as such, the order in which they will be called to handle events. 215 216 This also affects in what order they appear in the configuration file. 217 +/ 218 struct Priority 219 { 220 /++ 221 Numerical priority value. Lower is higher. 222 +/ 223 int value; 224 225 /++ 226 Helper `opUnary` to allow for `-10.priority`, instead of having to do the 227 (more correct) `(-10).priority`. 228 229 Example: 230 --- 231 mixin PluginRegistration!(MyPlugin, -10.priority); 232 --- 233 234 Params: 235 op = Operator. 236 237 Returns: 238 A new [Priority] with a [Priority.value|value] equal to the negative of this one's. 239 +/ 240 auto opUnary(string op: "-")() const 241 { 242 return Priority(-value); 243 } 244 } 245 246 247 // priority 248 /++ 249 Helper alias to use the proper style guide and still be able to instantiate 250 [Priority] instances with UFCS. 251 252 Example: 253 --- 254 mixin PluginRegistration!(MyPlugin, 50.priority); 255 --- 256 +/ 257 alias priority = Priority;