2024-11-25 01:25:19 +08:00
|
|
|
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 01:25:19 +08:00
|
|
|
{
|
2024-11-25 04:04:04 +08:00
|
|
|
throw new InvalidOperationException($"Config version mismatch: expected {ConfigMigrationManager.Instance.LatestVersion}, got {configVersion}");
|
2024-11-25 01:25:19 +08:00
|
|
|
}
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|