1 module vibed_rest_axios.rest_axios; 2 3 import vibe.web.internal.rest.common; 4 import vibe.web.rest: RestInterfaceSettings; 5 6 7 void genRestAxios (I) (RestInterfaceSettings vibeRestSettings, string dir = "./", string name = null) { 8 import std.file: copy; 9 import std.stdio: File; 10 11 if (name == null) name = I.stringof; 12 13 string outData = generateInterface!I(vibeRestSettings); 14 outData = genMainTemplate!"main.mixin.js"(name, outData); 15 16 // create files 17 auto f = File(dir~"/"~name~".js", "w"); 18 f.write(outData); 19 f.close(); 20 } 21 22 string generateInterface (I) (RestInterfaceSettings vibeRestSettings) 23 if (is(I == interface)) 24 { 25 import std.conv : to; 26 import std.traits : FunctionTypeOf, ReturnType; 27 28 import vibe.http.common : HTTPMethod; 29 import vibe.inet.url: URL; 30 import vibe.data.json; 31 import vibe.data.serialization: serialize; 32 33 string getFmtParam(ref const PathPart p) 34 { 35 if (!p.isParameter) return serialize!JsonSerializer(p.text).toString(); 36 return "toRestString("~p.text~")"; 37 } 38 39 string outData = ""; 40 auto vibeIntf = RestInterface!I(vibeRestSettings, true); 41 42 foreach (i, SI; vibeIntf.SubInterfaceTypes) { 43 string name = __traits(identifier, vibeIntf.SubInterfaceFunctions[i]); 44 if (i != 0) outData ~= ",\n"; 45 outData ~= "'" ~ name ~ "': { " ~ generateInterface!SI(vibeIntf.subInterfaces[i].settings) ~ " }"; 46 } 47 if (vibeIntf.SubInterfaceTypes.length != 0) outData ~= ",\n"; 48 49 foreach (i, F; vibeIntf.RouteFunctions) { 50 alias FT = FunctionTypeOf!F; 51 auto route = vibeIntf.routes[i]; 52 53 54 string url = ""; 55 56 // url assembly 57 if (route.pathHasPlaceholders) { 58 auto burl = URL(vibeIntf.baseURL); 59 if (burl.host.length) { 60 // extract the server part of the URL 61 burl.pathString = "/"; 62 url ~= serialize!JsonSerializer((burl.toString()[0 .. $-1])).toString() ~ " + "; 63 } 64 // and then assemble the full path piece-wise 65 66 // if route.pathHasPlaceholders no need to check route.fullPathParts.length 67 // because it fills in module vibe.web.internal.rest.common at 208 line only 68 url ~= getFmtParam(route.fullPathParts[0]); 69 foreach (p; route.fullPathParts[1..$]) { 70 url ~= " + "~ getFmtParam(p); 71 } 72 } else { 73 url = "'"~concatURL(vibeIntf.baseURL, route.pattern)~"'"; 74 } 75 76 // query parameters 77 if (route.queryParameters.length) { 78 foreach (j, p; route.queryParameters) { 79 url ~= ` + "` ~ (j == 0 ? `?` : `&`) ~ p.fieldName ~ `=" + toRestString(` ~ p.name ~ `)`; 80 } 81 } 82 83 // body parameters 84 string bodyParams = ""; 85 if (route.wholeBodyParameter.name.length) { 86 bodyParams = "'"~route.wholeBodyParameter.name~"'"; 87 } else if (route.bodyParameters.length) { 88 bodyParams = paramsToStr(route.bodyParameters); 89 } 90 91 // header parameters 92 string headers = ""; 93 if (route.headerParameters.length > 0) { 94 headers = paramsToStr(route.headerParameters); 95 } 96 97 string axiosTmpl = genAxiosTemplate!"axios.mixin.js"(url, route.method.to!string, headers, bodyParams); 98 string funParams = ""; 99 foreach (j, p; route.parameters) { 100 if (j != 0) funParams ~= ","; 101 funParams ~= p.name; 102 } 103 if (i != 0) outData ~= ",\n"; 104 outData ~= genMethodTemplate!"method.mixin.js"(route.functionName, funParams, axiosTmpl); 105 } 106 107 return outData; 108 } 109 110 string paramsToStr (in Parameter[] params) { 111 import vibe.data.json : Json; 112 113 string str = ""; 114 foreach (j, p; params) { 115 if (j != 0) str ~= ", "; 116 str ~= Json(p.fieldName).toString()~": "~p.name; 117 } 118 return "{" ~ str ~ "}"; 119 } 120 121 private string genAxiosTemplate (string path)(in string url, in string method, in string headers, in string data) 122 { 123 return mixin("`" ~ import(path) ~ "`"); 124 } 125 126 private string genMainTemplate (string path)(in string name, in string obBody) 127 { 128 return mixin("`" ~ import(path) ~ "`"); 129 } 130 131 private string genMethodTemplate (string path)(in string name, in string params, in string result) 132 { 133 return mixin("`" ~ import(path) ~ "`"); 134 } 135 136 137 package string concatURL(string prefix, string url, bool trailing = false) 138 @safe { 139 import std.algorithm : startsWith, endsWith; 140 141 auto pre = prefix.endsWith("/"); 142 auto post = url.startsWith("/"); 143 144 if (!url.length) return trailing && !pre ? prefix ~ "/" : prefix; 145 146 auto suffix = trailing && !url.endsWith("/") ? "/" : null; 147 148 if (pre) { 149 // "/" is ASCII, so can just slice 150 if (post) return prefix ~ url[1 .. $] ~ suffix; 151 else return prefix ~ url ~ suffix; 152 } else { 153 if (post) return prefix ~ url ~ suffix; 154 else return prefix ~ "/" ~ url ~ suffix; 155 } 156 }