Skip to content

Commit 3a6a953

Browse files
committed
feat: Add a pure JSON backend to Configy
This way we can parse things that are not YAML 1.1 compliant.
1 parent b0578bc commit 3a6a953

3 files changed

Lines changed: 122 additions & 0 deletions

File tree

build-files.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ source/dub/generators/targetdescription.d
1919
source/dub/generators/visuald.d
2020
source/dub/init.d
2121
source/dub/internal/configy/attributes.d
22+
source/dub/internal/configy/backend/json.d
2223
source/dub/internal/configy/backend/node.d
2324
source/dub/internal/configy/backend/yaml.d
2425
source/dub/internal/configy/easy.d
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*******************************************************************************
2+
3+
A backend using `dub.internal.vibecompat.data.json` as parser.
4+
5+
This is a more JSON-compliant parser than the YAML one (which is used for
6+
parsing `dub.json` files) however it doesn't provide location informations
7+
that are as good as the YAML parser, hence it is only used for internal
8+
files that may have 'special' UTF-8 characters.
9+
10+
See_Also:
11+
https://github.com/dlang-community/D-YAML/issues/342
12+
13+
*******************************************************************************/
14+
15+
module dub.internal.configy.backend.json;
16+
17+
import dub.internal.configy.backend.node;
18+
import dub.internal.configy.utils;
19+
import dub.internal.vibecompat.data.json;
20+
21+
import std.algorithm : among;
22+
import std.exception;
23+
import std.format;
24+
25+
/*******************************************************************************
26+
27+
A wrapper around a JSON node
28+
29+
This node is all types at once, however users should use one of the `asX`
30+
method to get some useful value out of it.
31+
32+
*******************************************************************************/
33+
34+
public class JSONNode : Node, Mapping, Sequence, Scalar {
35+
/// Underlying data
36+
private Json n;
37+
///
38+
private string file;
39+
40+
public this (Json node, string file) @safe {
41+
this.n = node;
42+
this.file = file;
43+
}
44+
45+
public override Location location () const scope @safe nothrow {
46+
// No column information
47+
version (JsonLineNumbers)
48+
return Location(this.file, this.n.line);
49+
else
50+
return Location(this.file, 0);
51+
}
52+
53+
public override Type type () const scope @safe nothrow {
54+
final switch (this.n.type()) {
55+
// Treat `null` and `undefined` as empty mapping
56+
case Json.Type.null_:
57+
case Json.Type.undefined:
58+
case Json.Type.object:
59+
return Node.Type.Mapping;
60+
case Json.Type.array:
61+
return Node.Type.Sequence;
62+
case Json.Type.bool_:
63+
case Json.Type.int_:
64+
case Json.Type.bigInt:
65+
case Json.Type.float_:
66+
case Json.Type.string:
67+
return Node.Type.Scalar;
68+
}
69+
}
70+
71+
public override inout(Mapping) asMapping () inout scope return @safe {
72+
return this.type() == Node.Type.Mapping ? this : null;
73+
}
74+
public override inout(Sequence) asSequence () inout scope return @safe {
75+
return this.type() == Node.Type.Sequence ? this : null;
76+
}
77+
public override inout(Scalar) asScalar () inout scope return @safe {
78+
// undefined and null can be used as scalar if one so desire
79+
return this.n.type().among(Json.Type.object, Json.Type.array) ? null : this;
80+
}
81+
82+
public override string str () const scope return @safe {
83+
return this.n.to!string;
84+
}
85+
public override size_t length () const scope @safe {
86+
return this.n.length();
87+
}
88+
public override int opApply (scope MapIterator dg) scope {
89+
foreach (ref string idx, ref Json val; this.n) {
90+
scope nIdx = new JSONNode(Json(idx), this.file);
91+
version (JsonLineNumbers) nIdx.n.line = val.line;
92+
scope Node nVal = new JSONNode(val, this.file);
93+
if (auto res = dg(nIdx, nVal))
94+
return res;
95+
}
96+
return 0;
97+
}
98+
public override int opApply (scope SeqIterator dg) scope {
99+
foreach (size_t idx, ref Json val; this.n) {
100+
scope nVal = new JSONNode(val, this.file);
101+
if (auto res = dg(idx, nVal))
102+
return res;
103+
}
104+
return 0;
105+
}
106+
}

source/dub/internal/configy/easy.d

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,3 +205,18 @@ public ConfigT parseConfigString (ConfigT)
205205
auto root = parseString(data, args.config_path);
206206
return parseConfig!ConfigT(root, strict);
207207
}
208+
209+
/// ditto
210+
public ConfigT parseConfigStringJSON (ConfigT)
211+
(string data, string path, StrictMode strict = StrictMode.Error)
212+
{
213+
import std.exception;
214+
import dub.internal.configy.backend.json;
215+
import dub.internal.vibecompat.data.json;
216+
217+
assert(path.length, "No path provided to parseConfigStringJSON");
218+
auto root = new JSONNode(parseJsonString(data, path), path).asMapping();
219+
enforce(root !is null, "Parsing '" ~ path ~ "' didn't yield an object");
220+
return parseConfig!ConfigT(root, strict);
221+
222+
}

0 commit comments

Comments
 (0)