1 module workspaced.api; 2 3 import std.conv; 4 import std.json; 5 import std.file; 6 import std.path; 7 import std.regex; 8 import core.time; 9 import painlessjson; 10 import standardpaths; 11 12 /// 13 alias AsyncCallback = void delegate(Throwable, JSONValue); 14 15 /// Will get called asynchronously (Must prepend AsyncCallback as argument) 16 enum async = 2603248026; 17 18 /// Will get called for loading components 19 enum load = 2603248027; 20 21 /// Will get called for unloading components 22 enum unload = 2603248028; 23 24 /// Will call this function in any case (cmd: component) 25 enum any = 2603248029; 26 27 /// Component call 28 struct component 29 { 30 /// Name of the component 31 string name; 32 } 33 34 /// Will get called when some argument matches 35 struct Arguments 36 { 37 /// Arguments to match 38 Argument[] arguments; 39 } 40 41 private struct Argument 42 { 43 /// Key in JSON object node at root level to match 44 string key; 45 /// Value in JSON object node at root level to match 46 JSONValue value; 47 } 48 49 private template ArgumentPair(size_t i) 50 { 51 static if (i > 0) 52 enum ArgumentPair = "ret.arguments[" ~ (i / 2 - 1) 53 .to!string ~ "] = Argument(args[" ~ (i - 2).to!string ~ "], args[" ~ (i - 1) 54 .to!string ~ "].toJSON);" ~ ArgumentPair!(i - 2); 55 else 56 enum ArgumentPair = ""; 57 } 58 59 package Arguments arguments(T...)(T args) 60 { 61 if (args.length < 2) 62 return Arguments.init; 63 Arguments ret; 64 ret.arguments.length = args.length / 2; 65 mixin(ArgumentPair!(args.length)); 66 return ret; 67 } 68 69 unittest 70 { 71 Arguments args = arguments("foo", 5, "bar", "str"); 72 assert(args.arguments[0].key == "foo"); 73 assert(args.arguments[0].value.integer == 5); 74 assert(args.arguments[1].key == "bar"); 75 assert(args.arguments[1].value.str == "str"); 76 } 77 78 package bool getConfigPath(string file, ref string retPath) 79 { 80 foreach (dir; standardPaths(StandardPath.config, "workspace-d")) 81 { 82 auto path = buildPath(dir, file); 83 if (path.exists) 84 { 85 retPath = path; 86 return true; 87 } 88 } 89 return false; 90 } 91 92 alias ImportPathProvider = string[]function(); 93 94 private string[] noImports() 95 { 96 return []; 97 } 98 99 ImportPathProvider importPathProvider = &noImports, stringImportPathProvider = &noImports; 100 101 enum verRegex = ctRegex!`(\d+)\.(\d+)\.(\d+)`; 102 bool checkVersion(string ver, int[3] target) 103 { 104 auto match = ver.matchFirst(verRegex); 105 assert(match); 106 int major = match[1].to!int; 107 int minor = match[2].to!int; 108 int patch = match[3].to!int; 109 if (major > target[0]) 110 return true; 111 if (major == target[0] && minor >= target[1]) 112 return true; 113 if (major == target[0] && minor == target[1] && patch >= target[2]) 114 return true; 115 return false; 116 } 117 118 alias BroadcastCallback = void function(JSONValue); 119 /// Broadcast callback which might get called by commands. For example when a component is outdated. Will be called in caller thread of function / while function executes. 120 BroadcastCallback broadcastCallback; 121 /// Must get called in caller thread 122 package void broadcast(JSONValue value) 123 { 124 if (broadcastCallback) 125 broadcastCallback(value); 126 else 127 throw new Exception("broadcastCallback not set!"); 128 } 129 130 package string getVersionAndFixPath(ref string execPath) 131 { 132 import std.process; 133 134 try 135 { 136 return execute([execPath, "--version"]).output; 137 } 138 catch (ProcessException e) 139 { 140 auto newPath = buildPath(thisExePath.dirName, execPath.baseName); 141 if (exists(newPath)) 142 { 143 execPath = newPath; 144 return execute([execPath, "--version"]).output; 145 } 146 throw e; 147 } 148 } 149 150 /// Calls an asynchronous function and blocks until it returns using Thread.sleep 151 JSONValue syncBlocking(alias fn, alias sleepDur = 1.msecs, Args...)(Args args) 152 { 153 import core.thread; 154 155 Throwable ex; 156 JSONValue ret; 157 bool done = false; 158 AsyncCallback cb = (err, data) { ex = err; ret = data; done = true; }; 159 fn(cb, args); 160 while (!done) 161 Thread.sleep(sleepDur); 162 if (ex) 163 throw ex; 164 return ret; 165 } 166 167 /// Calls an asynchronous function and blocks until it returns using Fiber.yield 168 JSONValue syncYield(alias fn, Args...)(Args args) 169 { 170 import core.thread; 171 172 Throwable ex; 173 JSONValue ret; 174 bool done = false; 175 AsyncCallback cb = (err, data) { ex = err; ret = data; done = true; }; 176 fn(cb, args); 177 while (!done) 178 Fiber.yield; 179 if (ex) 180 throw ex; 181 return ret; 182 }