- #nullable enable
- using IPA.Config.Data;
- using IPA.Logging;
- using System;
- using System.Linq;
- using System.IO;
- using System.Collections.Generic;
- using System.Text.Json;
- using System.Text.Json.Nodes;
- using Boolean = IPA.Config.Data.Boolean;
-
- namespace IPA.Config.Providers
- {
- internal class JsonConfigProvider : IConfigProvider
- {
- public static void RegisterConfig()
- {
- Config.Register<JsonConfigProvider>();
- }
-
- public string Extension => "json";
-
- public Value? Load(FileInfo file)
- {
- if (!file.Exists) return Value.Null();
-
- try
- {
- using var fileStream = file.OpenRead();
- return VisitToValue(JsonNode.Parse(fileStream));
- }
- catch (Exception e)
- {
- Logger.Config.Error($"Error reading JSON file {file.FullName}; ignoring");
- Logger.Config.Error(e);
- return Value.Null();
- }
- }
-
- private Value? VisitToValue(JsonNode? node)
- {
- if (node == null) return Value.Null();
-
- switch (node.GetValueKind())
- {
- case JsonValueKind.Undefined:
- Logger.Config.Warn($"Found {nameof(JsonValueKind)}.{nameof(JsonValueKind.Undefined)}");
- goto case JsonValueKind.Null;
- case JsonValueKind.Null:
- return Value.Null();
- case JsonValueKind.True:
- case JsonValueKind.False:
- if (node.AsValue().TryGetValue<bool>(out var b))
- return Value.Bool(b);
- return Value.Bool(false);
- case JsonValueKind.String:
- if (node.AsValue().TryGetValue<string>(out var s))
- return Value.Text(s);
- return Value.Text(string.Empty);
- case JsonValueKind.Number:
- var value = node.AsValue();
- if (value.TryGetValue<long>(out var l))
- return Value.Integer(l);
- if (value.TryGetValue<ulong>(out var u))
- return Value.Integer((long)u);
- if (value.TryGetValue<decimal>(out var dec))
- return Value.Float(dec);
- if (value.TryGetValue<double>(out var dou))
- return Value.Float((decimal)dou);
- if (value.TryGetValue<float>(out var flo))
- return Value.Float((decimal)flo);
- return Value.Float(0); // default to 0 if something breaks
- case JsonValueKind.Array:
- return Value.From(node.AsArray().Select(VisitToValue));
- case JsonValueKind.Object:
- return Value.From(node.AsObject()
- .Select(kvp => new KeyValuePair<string, Value?>(kvp.Key, VisitToValue(kvp.Value))));
- default:
- throw new ArgumentException($"Unknown {nameof(JsonValueKind)} in parameter");
- }
- }
-
- public void Store(Value value, FileInfo file)
- {
- if (!file.Directory.Exists)
- file.Directory.Create();
-
- try
- {
- var jsonNode = VisitToNode(value);
- using var fileStream = file.Open(FileMode.Create, FileAccess.Write);
- using var jsonWriter = new Utf8JsonWriter(fileStream, new JsonWriterOptions { Indented = true });
-
- if (jsonNode == null)
- {
- jsonWriter.WriteNullValue();
- }
- else
- {
- jsonNode.WriteTo(jsonWriter);
- }
- }
- catch (Exception e)
- {
- Logger.Config.Error($"Error serializing value for {file.FullName}");
- Logger.Config.Error(e);
- }
- }
-
- private JsonNode? VisitToNode(Value? val)
- {
- switch (val)
- {
- case Text t:
- return JsonValue.Create(t.Value);
- case Boolean b:
- return JsonValue.Create(b.Value);
- case Integer i:
- return JsonValue.Create(i.Value);
- case FloatingPoint f:
- return JsonValue.Create(f.Value);
- case List l:
- var jarr = new JsonArray();
- foreach (var tok in l.Select(VisitToNode)) jarr.Add(tok);
- return jarr;
- case Map m:
- var jobj = new JsonObject();
- foreach (var kvp in m) jobj.Add(kvp.Key, VisitToNode(kvp.Value));
- return jobj;
- case null:
- return null;
- default:
- throw new ArgumentException($"Unsupported subtype of {nameof(Value)}");
- }
- }
- }
- }
|