1 /** 2 * The module implements plugin system 3 * 4 * Copyright: (c) 2015-2020, Milofon Project. 5 * License: Subject to the terms of the BSD 3-Clause License, as written in the included LICENSE.md file. 6 * Author: <m.galanin@milofon.pro> Maksim Galanin 7 * Date: 2020-03-27 8 */ 9 10 module dango.system.plugin; 11 12 public 13 { 14 import BrightProof : SemVer; 15 import commandr : Program, ProgramArgs; 16 } 17 18 private 19 { 20 import std.traits; 21 22 import dango.system.logging : logInfo; 23 import dango.inject : DependencyContainer, inject; 24 import dango.inject.provider : ClassProvider; 25 import dango.system.exception; 26 } 27 28 29 /** 30 * Интерфейс плагина 31 */ 32 interface Plugin 33 { 34 /** 35 * Свойство возвращает наименование плагина 36 */ 37 string name() pure @safe nothrow; 38 39 /** 40 * Свойство возвращает версию приложения 41 */ 42 SemVer release() pure @safe nothrow; 43 } 44 45 46 /** 47 * Интерфейс контейнера плагинов 48 */ 49 interface PluginContainer(P : Plugin) 50 { 51 /** 52 * Обработка контейнером нового плагина 53 * 54 * Params: 55 * plugin = Плагин для добавления 56 */ 57 void collectPlugin(P plugin) @safe; 58 } 59 60 61 /** 62 * Plugin context 63 */ 64 interface PluginContext(P : Plugin, A...) 65 { 66 void registerPlugins(P plugin, A args) @safe; 67 } 68 69 70 /** 71 * Register plugin context 72 */ 73 void registerContext(Context : PluginContext!(P, A), P : Plugin, A...)(P plugin, A args) @safe 74 { 75 auto ctx = new Context(); 76 ctx.registerPlugins(plugin, args); 77 } 78 79 80 /** 81 * Плагин поддерживающий обработку консольных команд 82 */ 83 interface ConsolePlugin : Plugin 84 { 85 /** 86 * Свойство возвращает описание плагина 87 */ 88 string summary() pure @safe nothrow; 89 90 /** 91 * Регистрация обработчика команды 92 * 93 * Params: 94 * prog = Объект программы 95 */ 96 void registerCommand(Program prog) @safe; 97 98 /** 99 * Получение параметров командной строки 100 * 101 * Params: 102 * pArgs = Обработчик аргументов 103 */ 104 int runCommand(ProgramArgs pArgs) @safe; 105 } 106 107 108 /** 109 * Плагин поддерживающий работу в фоновом режиме 110 */ 111 interface DaemonPlugin : Plugin 112 { 113 /** 114 * Запуск процесса 115 */ 116 int startDaemon(); 117 118 /** 119 * Остановка процесса 120 * 121 * Params: 122 * exitStatus = Код завершения приложения 123 */ 124 int stopDaemon(int exitStatus); 125 } 126 127 128 /** 129 * Менеджер плагинов 130 */ 131 class PluginManager 132 { 133 private 134 { 135 alias PluginCollect = void delegate(Plugin) @safe; 136 137 struct PluginInfo 138 { 139 TypeInfo info; 140 Plugin plugin; 141 } 142 143 DependencyContainer _dcontainer; 144 PluginCollect[][TypeInfo] _containers; 145 PluginInfo[] _registrations; 146 } 147 148 149 /** 150 * Main constructor 151 */ 152 this(DependencyContainer container) @safe nothrow 153 { 154 this._dcontainer = container; 155 } 156 157 /** 158 * Регистрация плагина 159 */ 160 P registerPlugin(P : Plugin)() @safe 161 if (!is(P == Plugin)) 162 { 163 P plugin; 164 auto prov = new ClassProvider!(Plugin, P)(_dcontainer); 165 prov.withProvided(true, (val) @trusted { 166 plugin = cast(P)(*(cast(Object*)val)); 167 }); 168 return registerPlugin!P(plugin); 169 } 170 171 /** 172 * Регистрация плагина 173 */ 174 P registerPlugin(P : Plugin)(P plugin) @safe 175 if (!is(P == Plugin)) 176 { 177 static foreach (PI; InterfacesTuple!P) 178 { 179 static if (is(PI : PluginContainer!I, I : Plugin)) 180 registerPluginContainer!PI(plugin); 181 static if (is(PI : Plugin) && !is(PI == Plugin)) 182 _registrations ~= PluginInfo(typeid(PI), plugin); 183 } 184 return plugin; 185 } 186 187 /** 188 * Регистрация контейнера плагинов 189 */ 190 void registerPluginContainer(C : PluginContainer!P, P)(C containerPlugin) @safe 191 { 192 _containers[typeid(P)] ~= delegate void(Plugin p) { 193 containerPlugin.collectPlugin(cast(P)p); 194 }; 195 } 196 197 /** 198 * Инициализация плагинов 199 */ 200 void initializePlugins() @trusted 201 { 202 foreach (reg; _registrations) 203 { 204 if (reg.plugin is null || reg.info is null) 205 throw new DangoPluginException("Error registration plugin"); 206 207 if (auto collectors = reg.info in _containers) 208 { 209 logInfo("Use plugin %s (%s) as %s", reg.plugin.name, reg.plugin.release, reg.info); 210 foreach (collector; *collectors) 211 collector(reg.plugin); 212 } 213 else 214 throw new DangoPluginException("Error initialize plugin. " 215 ~ "Not found container for plugin " ~ reg.info.toString); 216 } 217 } 218 } 219