TomlTypeConverter.cs 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Globalization;
  4. using System.Text.RegularExpressions;
  5. namespace BepInEx.Configuration
  6. {
  7. /// <summary>
  8. /// Serializer/deserializer used by the config system.
  9. /// </summary>
  10. public static class TomlTypeConverter
  11. {
  12. private static Dictionary<Type, TypeConverter> TypeConverters { get; } = new Dictionary<Type, TypeConverter>
  13. {
  14. [typeof(string)] = new TypeConverter
  15. {
  16. ConvertToString = (obj, type) => Regex.Escape((string)obj),
  17. ConvertToObject = (str, type) =>
  18. {
  19. // Check if the string is a file path with unescaped \ path separators (e.g. D:\test and not D:\\test)
  20. if (Regex.IsMatch(str, @"^""?\w:\\(?!\\)(?!.+\\\\)"))
  21. return str;
  22. return Regex.Unescape(str);
  23. },
  24. },
  25. [typeof(bool)] = new TypeConverter
  26. {
  27. ConvertToString = (obj, type) => obj.ToString().ToLowerInvariant(),
  28. ConvertToObject = (str, type) => bool.Parse(str),
  29. },
  30. [typeof(byte)] = new TypeConverter
  31. {
  32. ConvertToString = (obj, type) => obj.ToString(),
  33. ConvertToObject = (str, type) => byte.Parse(str),
  34. },
  35. //integral types
  36. [typeof(sbyte)] = new TypeConverter
  37. {
  38. ConvertToString = (obj, type) => obj.ToString(),
  39. ConvertToObject = (str, type) => sbyte.Parse(str),
  40. },
  41. [typeof(byte)] = new TypeConverter
  42. {
  43. ConvertToString = (obj, type) => obj.ToString(),
  44. ConvertToObject = (str, type) => byte.Parse(str),
  45. },
  46. [typeof(short)] = new TypeConverter
  47. {
  48. ConvertToString = (obj, type) => obj.ToString(),
  49. ConvertToObject = (str, type) => short.Parse(str),
  50. },
  51. [typeof(ushort)] = new TypeConverter
  52. {
  53. ConvertToString = (obj, type) => obj.ToString(),
  54. ConvertToObject = (str, type) => ushort.Parse(str),
  55. },
  56. [typeof(int)] = new TypeConverter
  57. {
  58. ConvertToString = (obj, type) => obj.ToString(),
  59. ConvertToObject = (str, type) => int.Parse(str),
  60. },
  61. [typeof(uint)] = new TypeConverter
  62. {
  63. ConvertToString = (obj, type) => obj.ToString(),
  64. ConvertToObject = (str, type) => uint.Parse(str),
  65. },
  66. [typeof(long)] = new TypeConverter
  67. {
  68. ConvertToString = (obj, type) => obj.ToString(),
  69. ConvertToObject = (str, type) => long.Parse(str),
  70. },
  71. [typeof(ulong)] = new TypeConverter
  72. {
  73. ConvertToString = (obj, type) => obj.ToString(),
  74. ConvertToObject = (str, type) => ulong.Parse(str),
  75. },
  76. //floating point types
  77. [typeof(float)] = new TypeConverter
  78. {
  79. ConvertToString = (obj, type) => ((float)obj).ToString(NumberFormatInfo.InvariantInfo),
  80. ConvertToObject = (str, type) => float.Parse(str, NumberFormatInfo.InvariantInfo),
  81. },
  82. [typeof(double)] = new TypeConverter
  83. {
  84. ConvertToString = (obj, type) => ((double)obj).ToString(NumberFormatInfo.InvariantInfo),
  85. ConvertToObject = (str, type) => double.Parse(str, NumberFormatInfo.InvariantInfo),
  86. },
  87. [typeof(decimal)] = new TypeConverter
  88. {
  89. ConvertToString = (obj, type) => ((decimal)obj).ToString(NumberFormatInfo.InvariantInfo),
  90. ConvertToObject = (str, type) => decimal.Parse(str, NumberFormatInfo.InvariantInfo),
  91. },
  92. //enums are special
  93. [typeof(Enum)] = new TypeConverter
  94. {
  95. ConvertToString = (obj, type) => obj.ToString(),
  96. ConvertToObject = (str, type) => Enum.Parse(type, str, true),
  97. },
  98. };
  99. /// <summary>
  100. /// Convert object of a given type to a string using available converters.
  101. /// </summary>
  102. public static string ConvertToString(object value, Type valueType)
  103. {
  104. var conv = GetConverter(valueType);
  105. if (conv == null)
  106. throw new InvalidOperationException($"Cannot convert from type {valueType}");
  107. return conv.ConvertToString(value, valueType);
  108. }
  109. /// <summary>
  110. /// Convert string to an object of a given type using available converters.
  111. /// </summary>
  112. public static T ConvertToValue<T>(string value)
  113. {
  114. return (T)ConvertToValue(value, typeof(T));
  115. }
  116. /// <summary>
  117. /// Convert string to an object of a given type using available converters.
  118. /// </summary>
  119. public static object ConvertToValue(string value, Type valueType)
  120. {
  121. var conv = GetConverter(valueType);
  122. if (conv == null)
  123. throw new InvalidOperationException($"Cannot convert to type {valueType.Name}");
  124. return conv.ConvertToObject(value, valueType);
  125. }
  126. /// <summary>
  127. /// Get a converter for a given type if there is any.
  128. /// </summary>
  129. public static TypeConverter GetConverter(Type valueType)
  130. {
  131. if (valueType == null) throw new ArgumentNullException(nameof(valueType));
  132. if (valueType.IsEnum)
  133. return TypeConverters[typeof(Enum)];
  134. TypeConverters.TryGetValue(valueType, out var result);
  135. return result;
  136. }
  137. /// <summary>
  138. /// Add a new type converter for a given type.
  139. /// </summary>
  140. public static void AddConverter(Type type, TypeConverter converter)
  141. {
  142. if (type == null) throw new ArgumentNullException(nameof(type));
  143. if (converter == null) throw new ArgumentNullException(nameof(converter));
  144. if (CanConvert(type)) throw new ArgumentException("The specified type already has a converter assigned to it", nameof(type));
  145. TypeConverters.Add(type, converter);
  146. }
  147. /// <summary>
  148. /// Check if a given type can be converted to and from string.
  149. /// </summary>
  150. public static bool CanConvert(Type type)
  151. {
  152. return GetConverter(type) != null;
  153. }
  154. /// <summary>
  155. /// Give a list of types with registered converters.
  156. /// </summary>
  157. public static IEnumerable<Type> GetSupportedTypes()
  158. {
  159. return TypeConverters.Keys;
  160. }
  161. }
  162. }