1 module reggae.dub.json; 2 3 import reggae.dub.info; 4 import reggae.build; 5 import std.json; 6 import std.algorithm: map, filter; 7 8 9 DubInfo getDubInfo(string origString) @trusted { 10 import std.string: indexOf; 11 import core.exception: RangeError; 12 import std.array; 13 14 string nextOpenCurly(string str) { 15 return str[str.indexOf("{") .. $]; 16 } 17 18 try { 19 20 // the output might contain non-JSON at the beginning in stderr 21 auto jsonString = nextOpenCurly(origString); 22 23 for(; ; jsonString = nextOpenCurly(jsonString[1..$])) { 24 auto json = parseJSON(jsonString); 25 26 bool hasPackages() { 27 try 28 return ("packages" in json.object) !is null; 29 catch(JSONException ex) 30 return false; 31 } 32 33 if(!hasPackages) { 34 continue; 35 } 36 37 auto packages = json.byKey("packages"); 38 return DubInfo(packages.array. 39 map!(a => DubPackage(a.byKey("name").str, 40 a.byKey("path").str, 41 a.getOptional("mainSourceFile"), 42 a.getOptional("targetFileName"), 43 a.byKey("dflags").jsonValueToStrings, 44 a.byKey("lflags").jsonValueToStrings, 45 a.byKey("importPaths").jsonValueToStrings, 46 a.byKey("stringImportPaths").jsonValueToStrings, 47 a.byKey("files").jsonValueToFiles, 48 a.getOptional("targetType"), 49 a.getOptionalList("versions"), 50 a.getOptionalList("dependencies"), 51 a.getOptionalList("libs"), 52 a.byOptionalKey("active", true), //true for backwards compatibility 53 a.getOptionalList("preBuildCommands"), 54 )). 55 filter!(a => a.active). 56 array); 57 } 58 } catch(RangeError e) { 59 import std.stdio; 60 stderr.writeln("Could not parse the output of dub describe:\n", origString); 61 throw e; 62 } 63 } 64 65 66 private string[] jsonValueToFiles(JSONValue files) @trusted { 67 import std.array; 68 69 return files.array. 70 filter!(a => ("type" in a && a.byKey("type").str == "source") || 71 ("role" in a && a.byKey("role").str == "source") || 72 ("type" !in a && "role" !in a)). 73 map!(a => a.byKey("path").str). 74 array; 75 } 76 77 private string[] jsonValueToStrings(JSONValue json) @trusted { 78 import std.array; 79 return json.array.map!(a => a.str).array; 80 } 81 82 83 private JSONValue byKey(JSONValue json, in string key) @trusted { 84 return json.object[key]; 85 } 86 87 private bool byOptionalKey(JSONValue json, in string key, bool def) { 88 import std.conv: to; 89 auto value = json.object; 90 return key in value ? value[key].boolean : def; 91 } 92 93 //std.json has no conversion to bool 94 private bool boolean(JSONValue json) @trusted { 95 import std.exception: enforce; 96 enforce!JSONException(json.type == JSON_TYPE.TRUE || json.type == JSON_TYPE.FALSE, 97 "JSONValue is not a boolean"); 98 return json.type == JSON_TYPE.TRUE; 99 } 100 101 private string getOptional(JSONValue json, in string key) @trusted { 102 auto aa = json.object; 103 return key in aa ? aa[key].str : ""; 104 } 105 106 private string[] getOptionalList(JSONValue json, in string key) @trusted { 107 auto aa = json.object; 108 return key in aa ? aa[key].jsonValueToStrings : []; 109 }