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 }