AquaDX/AquaMai/AquaMai.Config/ConfigParser.cs

126 lines
4.5 KiB
C#
Raw Normal View History

using System;
using Tomlet.Models;
using AquaMai.Config.Interfaces;
using AquaMai.Config.Reflection;
using AquaMai.Config.Migration;
using System.Linq;
namespace AquaMai.Config;
public class ConfigParser : IConfigParser
{
public readonly static ConfigParser Instance = new();
private readonly static string[] supressUnrecognizedConfigPaths = ["Version"];
private readonly static string[] supressUnrecognizedConfigPathSuffixes = [
".Disabled", // For section enable state.
".Disable", // For section enable state, but the wrong key, warn later.
".Enabled", // For section enable state, but the wrong key, warn later.
".Enable", // For section enable state, but the wrong key, warn later.
];
private ConfigParser()
{}
public void Parse(IConfig config, string tomlString)
{
var configView = new ConfigView(tomlString);
Parse(config, configView);
}
public void Parse(IConfig config, IConfigView configView)
{
var configVersion = ConfigMigrationManager.Instance.GetVersion(configView);
2024-11-25 04:04:04 +08:00
if (configVersion != ConfigMigrationManager.Instance.LatestVersion)
{
2024-11-25 04:04:04 +08:00
throw new InvalidOperationException($"Config version mismatch: expected {ConfigMigrationManager.Instance.LatestVersion}, got {configVersion}");
}
Hydrate((Config)config, ((ConfigView)configView).root, "");
}
private static void Hydrate(Config config, TomlValue value, string path)
{
if (config.ReflectionManager.TryGetSection(path, out var section))
{
ParseSectionEnableState(config, (ReflectionManager.Section)section, value, path);
}
if (value is TomlTable table)
{
bool isLeaf = true;
foreach (var subKey in table.Keys)
{
var subValue = table.GetValue(subKey);
var subPath = path == "" ? subKey : $"{path}.{subKey}";
if (subValue is TomlTable)
{
isLeaf = false;
}
Hydrate(config, subValue, subPath);
}
// A leaf dictionary, which has no child dictionaries, must be a section.
if (isLeaf && section == null)
{
Utility.Log($"Unrecognized config section: {path}");
}
}
else
{
// It's an config entry value (or a primitive type for enabling a section).
if (!config.ReflectionManager.ContainsSection(path) &&
!config.ReflectionManager.ContainsEntry(path) &&
!supressUnrecognizedConfigPaths.Any(s => path.Equals(s, StringComparison.OrdinalIgnoreCase)) &&
!supressUnrecognizedConfigPathSuffixes.Any(suffix => path.EndsWith(suffix, StringComparison.OrdinalIgnoreCase)))
{
Utility.Log($"Unrecognized config entry: {path}");
return;
}
if (config.ReflectionManager.TryGetEntry(path, out var entry))
{
try
{
var parsedValue = Utility.ParseTomlValue(entry.Field.FieldType, value);
config.SetEntryValue(entry, parsedValue);
}
catch (Exception e)
{
Utility.Log($"Error hydrating config ({path} = {value.StringValue}): {e.Message}");
}
}
}
}
public static void ParseSectionEnableState(
Config config,
ReflectionManager.Section section,
TomlValue value,
string path)
{
if (value is TomlTable table)
{
foreach (var unexpectedKey in (string[]) ["Enable", "Enabled", "Disable"])
{
if (Utility.TomlContainsKeyCaseInsensitive(table, unexpectedKey))
{
Utility.Log($"Unexpected key \"{unexpectedKey}\" for enable status under \"{path}\". Only \"Disabled\" is parsed.");
}
}
if (Utility.TomlTryGetValueCaseInsensitive(table, "Disabled", out var disableValue) && !section.Attribute.AlwaysEnabled)
{
var disabled = Utility.IsTruty(disableValue, path + ".Disabled");
config.SetSectionEnabled(section, !disabled);
}
else
{
config.SetSectionEnabled(section, true);
}
}
else
{
config.SetSectionEnabled(section, Utility.IsTruty(value, path));
}
}
}