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