1 /* 2 * Copyright (c) 2017-2019 sel-project 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a copy 5 * of this software and associated documentation files (the "Software"), to deal 6 * in the Software without restriction, including without limitation the rights 7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 * copies of the Software, and to permit persons to whom the Software is 9 * furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in all 12 * copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 * SOFTWARE. 21 * 22 */ 23 /** 24 * Copyright: Copyright (c) 2017-2019 sel-project 25 * License: MIT 26 * Authors: Kripth 27 * Source: $(HTTP github.com/sel-project/selery/source/selery/plugin.d, selery/plugin.d) 28 */ 29 module selery.plugin; 30 31 import core.atomic : atomicOp; 32 33 import selery.about; 34 import selery.lang : Translatable; 35 36 private shared uint _id; 37 38 /** 39 * Informations about a plugin and registration-related 40 * utilities. 41 */ 42 class Plugin { 43 44 public immutable uint id; 45 46 /** 47 * Plugin's name as declared in plugin.toml. 48 */ 49 public immutable string name; 50 51 /** 52 * Plugin's absolute path, used to load plugin'a assets. 53 */ 54 public immutable string path; 55 56 /** 57 * Plugin's authors as declared in plugin.toml. 58 */ 59 public immutable string[] authors; 60 61 /** 62 * Plugin's version. 63 */ 64 public immutable string version_; 65 66 /** 67 * Indicates whether the plugin has a main class. 68 */ 69 public immutable bool main; 70 71 public void delegate()[] onstart, onreload, onstop; 72 73 public this(string name, string path, string[] authors, string version_, bool main) { 74 assert(version_.length); 75 this.id = atomicOp!"+="(_id, 1); 76 this.name = name; 77 this.path = path; 78 this.authors = authors.idup; 79 this.version_ = version_; 80 this.main = main; 81 } 82 83 } 84 85 // attributes for main classes 86 enum start; 87 enum reload; 88 enum stop; 89 90 // attributes for events 91 enum event; 92 enum global; 93 enum inherit; 94 enum cancel; 95 96 struct Description { 97 98 enum : ubyte { 99 100 EMPTY, 101 TEXT, 102 TRANSLATABLE 103 104 } 105 106 public ubyte type = EMPTY; 107 108 union { 109 110 string text; 111 Translatable translatable; 112 113 } 114 115 this(string text) { 116 this.type = TEXT; 117 this.text = text; 118 } 119 120 this(Translatable translatable) { 121 this.type = TRANSLATABLE; 122 this.translatable = translatable; 123 } 124 125 } 126 127 // attributes for commands 128 struct command { 129 130 string command; 131 string[] aliases; 132 Description description; 133 134 public this(string command, string[] aliases=[], Description description=Description.init) { 135 this.command = command; 136 this.aliases = aliases; 137 this.description = description; 138 } 139 140 public this(string command, string[] aliases, string description) { 141 this(command, aliases, Description(description)); 142 } 143 144 public this(string command, string[] aliases, Translatable description) { 145 this(command, aliases, Description(description)); 146 } 147 148 public this(string command, string description) { 149 this(command, [], description); 150 } 151 152 public this(string command, Translatable description) { 153 this(command, [], description); 154 } 155 156 } 157 158 struct permissionLevel { ubyte permissionLevel; } 159 enum op = permissionLevel(1); 160 struct permission { string[] permissions; this(string[] permissions...){ this.permissions = permissions; } } 161 alias permissions = permission; 162 enum hidden; 163 enum unimplemented; 164 165 void loadPluginAttributes(bool main, EventBase, GlobalEventBase, bool inheritance, CommandBase, bool tasks, T, S)(T class_, Plugin plugin, S storage) { 166 167 enum bool events = !is(typeof(EventBase) == bool); 168 enum bool globals = !is(typeof(GlobalEventBase) == bool); 169 enum bool commands = !is(typeof(CommandBase) == bool); 170 171 import std.traits : getSymbolsByUDA, hasUDA, getUDAs, Parameters; 172 173 foreach(member ; __traits(allMembers, T)) { 174 static if(is(typeof(__traits(getMember, T, member)) == function)) { //TODO must be public and not a template 175 mixin("alias F = T." ~ member ~ ";"); 176 enum del = "&class_." ~ member; 177 // start/stop 178 static if(main) { 179 static if(hasUDA!(F, start) && Parameters!F.length == 0) { 180 plugin.onstart ~= mixin(del); 181 } 182 static if(hasUDA!(F, reload) && Parameters!F.length == 0) { 183 plugin.onreload ~= mixin(del); 184 } 185 static if(hasUDA!(F, stop) && Parameters!F.length == 0) { 186 plugin.onstop ~= mixin(del); 187 } 188 } 189 // events 190 enum isValid(E) = is(Parameters!F[0] == interface) || is(Parameters!F[0] : E); 191 static if(events && Parameters!F.length == 1 && ((events && hasUDA!(F, event) && isValid!EventBase) || (globals && hasUDA!(F, global) && isValid!GlobalEventBase))) { 192 static if(hasUDA!(F, cancel)) { 193 //TODO event must be cancellable 194 auto ev = delegate(Parameters!F[0] e){ e.cancel(); }; 195 } else { 196 auto ev = mixin(del); 197 } 198 static if(events && hasUDA!(F, event)) { 199 storage.addEventListener(ev); 200 } 201 static if(globals && hasUDA!(F, global)) { 202 (cast()storage.globalListener).addEventListener(ev); 203 } 204 } 205 // commands 206 static if(commands && hasUDA!(F, command) && Parameters!F.length >= 1 && is(Parameters!F[0] : CommandBase)) { 207 enum c = getUDAs!(F, command)[0]; 208 static if(hasUDA!(F, permissionLevel)) enum pl = getUDAs!(F, permissionLevel)[0].permissionLevel; 209 else enum ubyte pl = 0; 210 static if(hasUDA!(F, permission)) enum p = getUDAs!(F, permission)[0].permissions; 211 else enum string[] p = []; 212 storage.registerCommand!F(mixin(del), c.command, c.description, c.aliases, pl, p, hasUDA!(F, hidden), !hasUDA!(F, unimplemented)); 213 } 214 } 215 } 216 217 }