Start work on config provider type conversion

This commit is contained in:
ghorsington committed 2021-07-04 15:16:42 +03:00
1 parent 441f302bf9
commit e90304073c
6 files changed
+107 -43

No files matched your search

@@ -1,5 +1,6 @@
using System;
using System.Linq;
using HarmonyLib;
namespace BepInEx.Configuration
{
@@ -11,7 +12,7 @@ namespace BepInEx.Configuration
/// <inheritdoc />
public class ConfigDefinition : IEquatable<ConfigDefinition>
{
private static readonly char[] _invalidConfigChars = { '=', '\n', '\t', '\\', '"', '\'', '[', ']' };
private static readonly char[] _invalidConfigChars = { '=', '\n', '\t', '\\', '"', '\'', '[', ']', '.' };
/// <summary>
/// Create a new definition. Definitions with same section and key are equal.
@@ -35,6 +36,8 @@ namespace BepInEx.Configuration
/// Name of the setting.
/// </summary>
public string Key { get; }
internal string[] ConfigPath => Section.Split('.').AddItem(Key).ToArray();
/// <summary>
/// Check if the definitions are the same.
@@ -50,12 +53,9 @@ namespace BepInEx.Configuration
private static void CheckInvalidConfigChars(string val, string name)
{
if (val == null) throw new ArgumentNullException(name);
if (val != val.Trim())
throw new ArgumentException("Cannot use whitespace characters at start or end of section and key names",
name);
if (val.Any(c => _invalidConfigChars.Contains(c)))
throw new
ArgumentException(@"Cannot use any of the following characters in section and key names: = \n \t \ "" ' [ ]",
ArgumentException(@"Cannot use any of the following characters in section and key names: = \n \t \ "" ' [ ] .",
name);
}
@@ -1,7 +1,10 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using BepInEx.Logging;
using HarmonyLib;
namespace BepInEx.Configuration
{
@@ -120,7 +123,7 @@ namespace BepInEx.Configuration
/// <summary>
/// Get the serialized representation of the value.
/// </summary>
public string GetSerializedValue() => TomlTypeConverter.ConvertToString(BoxedValue, SettingType);
public string GetSerializedValue() => ConfigTypeConverter.ConvertToString(BoxedValue, SettingType);
/// <summary>
/// Set the value by using its serialized form.
@@ -129,7 +132,7 @@ namespace BepInEx.Configuration
{
try
{
var newValue = TomlTypeConverter.ConvertToValue(value, SettingType);
var newValue = ConfigTypeConverter.ConvertToValue(value, SettingType);
BoxedValue = newValue;
}
catch (Exception e)
@@ -164,7 +167,7 @@ namespace BepInEx.Configuration
writer.WriteLine("# Setting type: " + SettingType.Name);
writer.WriteLine("# Default value: " + TomlTypeConverter.ConvertToString(DefaultValue, SettingType));
writer.WriteLine("# Default value: " + ConfigTypeConverter.ConvertToString(DefaultValue, SettingType));
if (Description.AcceptableValues != null)
{
+62 -11
View File
@@ -1,9 +1,11 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using BepInEx.Logging;
using HarmonyLib;
// TODO: Check if IDictionary implementation is reasonable
@@ -37,7 +39,7 @@ namespace BepInEx.Configuration
public static ConfigFile CoreConfig { get; } = new(Paths.BepInExConfigPath, true);
public IConfigurationProvider ConfigurationProvider { get; set; }
public IConfigurationProvider ConfigurationProvider { get; }
/// <summary>
/// All config entries inside
@@ -313,24 +315,15 @@ namespace BepInEx.Configuration
T defaultValue,
ConfigDescription configDescription = null)
{
if (!TomlTypeConverter.CanConvert(typeof(T)))
throw new
ArgumentException($"Type {typeof(T)} is not supported by the config system. Supported types: {string.Join(", ", TomlTypeConverter.GetSupportedTypes().Select(x => x.Name).ToArray())}");
lock (_ioLock)
{
if (Entries.TryGetValue(configDefinition, out var rawEntry))
return (ConfigEntry<T>) rawEntry;
var entry = new ConfigEntry<T>(this, configDefinition, defaultValue, configDescription);
Entries[configDefinition] = entry;
if (OrphanedEntries.TryGetValue(configDefinition, out var homelessValue))
{
entry.SetSerializedValue(homelessValue);
OrphanedEntries.Remove(configDefinition);
}
entry.BoxedValue = GetValue(configDefinition.ConfigPath, typeof(T)) ?? defaultValue;
if (SaveOnConfigSet)
Save();
@@ -338,6 +331,64 @@ namespace BepInEx.Configuration
return entry;
}
}
private object GetValue(string[] path, Type type)
{
// We have direct value; simply resolve it normally
if (ConfigTypeConverter.CanConvertDirectly(type))
{
var val = ConfigurationProvider.Get(path);
if (val != null)
return ConfigTypeConverter.ConvertToValue(val.Value, type);
return null;
}
else if (type.IsArray)
{
var elType = type.GetElementType();
var indexPath = path.AddItem("0").ToArray();
var values = new List<object>();
for (var i = 0;; i++)
{
indexPath[indexPath.Length - 1] = i.ToString(CultureInfo.InvariantCulture);
var val = GetValue(indexPath, elType);
if (val == null)
break;
values.Add(val);
}
var arr = Array.CreateInstance(elType, values.Count);
for (var i = 0; i < values.Count; i++)
arr.SetValue(values[i], i);
return arr;
}
else if (typeof(IList<>).IsAssignableFrom(type))
{
var res = Activator.CreateInstance(type);
var elType = type.GetGenericArguments()[0];
var add = MethodInvoker.GetHandler(AccessTools.Method(type, "Add", new[] { elType }));
var indexPath = path.AddItem("0").ToArray();
for (var i = 0;; i++)
{
indexPath[indexPath.Length - 1] = i.ToString(CultureInfo.InvariantCulture);
var val = GetValue(indexPath, elType);
if (val == null)
break;
add(val);
}
return res;
} else if (typeof(IDictionary<,>).IsAssignableFrom(type))
{
var res = Activator.CreateInstance(type);
var gArgs = type.GetGenericArguments();
if (gArgs[0] != typeof(string))
throw new Exception("Only dictionaries with string keys are supported");
var elType = gArgs[1];
var add = MethodInvoker.GetHandler(AccessTools.Method(type, "Add", new[] { typeof(string), elType }));
}
return null;
}
/// <summary>
/// Create a new setting. The setting is saved to drive and loaded automatically.
@@ -10,21 +10,21 @@ namespace BepInEx.Configuration
/// <summary>
/// Serializer/deserializer used by the config system.
/// </summary>
public static class TomlTypeConverter
public static class ConfigTypeConverter
{
// Don't put anything from UnityEngine here or it will break preloader, use LazyTomlConverterLoader instead
private static Dictionary<Type, TypeConverter> TypeConverters { get; } = new()
private static Dictionary<Type, TypeConverter> DirectConverters { get; } = new()
{
[typeof(string)] = new TypeConverter
{
ConvertToString = (obj, type) => Escape((string) obj),
ConvertToObject = (str, type) =>
{
// Check if the string is a file path with unescaped \ path separators (e.g. D:\test and not D:\\test)
if (Regex.IsMatch(str, @"^""?\w:\\(?!\\)(?!.+\\\\)"))
return str;
return Unescape(str);
}
ConvertToString = (obj, type) => obj.ToString(), //Escape((string) obj),
ConvertToObject = (str, type) => str,
// {
// // Check if the string is a file path with unescaped \ path separators (e.g. D:\test and not D:\\test)
// if (Regex.IsMatch(str, @"^""?\w:\\(?!\\)(?!.+\\\\)"))
// return str;
// return Unescape(str);
// }
},
[typeof(bool)] = new TypeConverter
{
@@ -98,15 +98,25 @@ namespace BepInEx.Configuration
ConvertToObject = (str, type) => decimal.Parse(str, NumberFormatInfo.InvariantInfo)
},
//enums are special
// Special direct converters
[typeof(Enum)] = new TypeConverter
{
ConvertToString = (obj, type) => obj.ToString(),
ConvertToObject = (str, type) => Enum.Parse(type, str, true)
},
[typeof(DateTime)] = new TypeConverter
{
ConvertToString = (obj, type) => ((DateTime) obj).ToString("O"),
ConvertToObject = (str, type) => DateTime.ParseExact(str, "O", CultureInfo.InvariantCulture)
},
[typeof(DateTimeOffset)] = new TypeConverter
{
ConvertToString = (obj, type) => ((DateTimeOffset) obj).ToString("O"),
ConvertToObject = (str, type) => DateTimeOffset.ParseExact(str, "O", CultureInfo.InvariantCulture)
}
};
/// <summary>
/// Convert object of a given type to a string using available converters.
/// </summary>
@@ -145,9 +155,9 @@ namespace BepInEx.Configuration
throw new ArgumentNullException(nameof(valueType));
if (valueType.IsEnum)
return TypeConverters[typeof(Enum)];
return DirectConverters[typeof(Enum)];
TypeConverters.TryGetValue(valueType, out var result);
DirectConverters.TryGetValue(valueType, out var result);
return result;
}
@@ -160,25 +170,25 @@ namespace BepInEx.Configuration
{
if (type == null) throw new ArgumentNullException(nameof(type));
if (converter == null) throw new ArgumentNullException(nameof(converter));
if (CanConvert(type))
if (CanConvertDirectly(type))
{
Logger.LogWarning("Tried to add a TomlConverter when one already exists for type " + type.FullName);
return false;
}
TypeConverters.Add(type, converter);
DirectConverters.Add(type, converter);
return true;
}
/// <summary>
/// Check if a given type can be converted to and from string.
/// </summary>
public static bool CanConvert(Type type) => GetConverter(type) != null;
public static bool CanConvertDirectly(Type type) => GetConverter(type) != null;
/// <summary>
/// Give a list of types with registered converters.
/// </summary>
public static IEnumerable<Type> GetSupportedTypes() => TypeConverters.Keys;
public static IEnumerable<Type> GetSupportedTypes() => DirectConverters.Keys;
private static string Escape(this string txt)
{
@@ -20,7 +20,7 @@ namespace BepInEx.Configuration
{
static KeyboardShortcut()
{
TomlTypeConverter.AddConverter(
ConfigTypeConverter.AddConverter(
typeof(KeyboardShortcut),
new TypeConverter
{
+5 -5
View File
@@ -24,7 +24,7 @@ namespace BepInEx.Unity
}
};
TomlTypeConverter.AddConverter(typeof(Color), colorConverter);
ConfigTypeConverter.AddConverter(typeof(Color), colorConverter);
var jsonConverter = new TypeConverter
{
@@ -32,10 +32,10 @@ namespace BepInEx.Unity
ConvertToObject = (str, type) => JsonUtility.FromJson(type: type, json: str)
};
TomlTypeConverter.AddConverter(typeof(Vector2), jsonConverter);
TomlTypeConverter.AddConverter(typeof(Vector3), jsonConverter);
TomlTypeConverter.AddConverter(typeof(Vector4), jsonConverter);
TomlTypeConverter.AddConverter(typeof(Quaternion), jsonConverter);
ConfigTypeConverter.AddConverter(typeof(Vector2), jsonConverter);
ConfigTypeConverter.AddConverter(typeof(Vector3), jsonConverter);
ConfigTypeConverter.AddConverter(typeof(Vector4), jsonConverter);
ConfigTypeConverter.AddConverter(typeof(Quaternion), jsonConverter);
}
}
}