1 module nxt.sourced;
2 
3 import nxt.path : Path, exists;
4 
5 /++ JSON value with origin path.
6 
7 	TODO: generalize (templated) to any value.
8 	TODO: generalize (templated) to any URL.
9  +/
10 struct SourcedJSON {
11 	import std.json : JSONValue, parseJSON;
12 	Path path;					///< Path of `value` serialized to string.
13 	JSONValue value;			///< JSON value.
14 	bool outdated;				///< Is true if value needs has been changed since read from path.
15 
16 	/++ Uses parameter `Path` in ctor instead of `fromFile`.
17 		See: https://docs.rs/from_file/latest/from_file/
18 	+/
19 	this(Path path, bool silentPassUponFailure = false) @safe {
20 		import std.file : readText;
21 		this.path = path;
22 		this.value = silentPassUponFailure ?
23 			(path.exists ?
24 				readText(path.str).parseJSON :
25 				JSONValue.emptyObject) :
26 			readText(path.str).parseJSON;
27 	}
28 
29 	~this() const {
30 		if (outdated)
31 			writeBack();
32 	}
33 
34 	void writeBack(in bool pretty = true) const {
35 		import std.stdio : File;
36 		import std.json : JSONOptions;
37 		if (pretty) {
38 			static if (__traits(hasMember, JSONValue, "toPrettyString")) {
39 				static if (is(typeof(value.toPrettyString(File(path, "w").lockingBinaryWriter, JSONOptions.doNotEscapeSlashes)))) {
40 					value.toPrettyString(File(path, "w").lockingBinaryWriter, JSONOptions.doNotEscapeSlashes);
41 				} else {
42 					File(path.str, "w").write(value.toPrettyString(JSONOptions.doNotEscapeSlashes));
43 				}
44 			} else {
45 				static assert(0);
46 			}
47 		} else {
48 			static if (__traits(hasMember, JSONValue, "toString")) {
49 				File(path.str, "w").write(value.toString(JSONOptions.doNotEscapeSlashes));
50 			} else {
51 				File(path.str, "w").write(value.to!string());
52 			}
53 		}
54 	}
55 }
56 
57 ///
58 @safe unittest {
59 	/+ TODO: const x = SourcedJSON(Path("")); +/
60 }