You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

724 lines
33 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. #nullable enable
  2. using IPA.Config.Data;
  3. using IPA.Config.Stores.Attributes;
  4. using IPA.Logging;
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Linq;
  8. using System.Text;
  9. using System.Threading.Tasks;
  10. using UnityEngine;
  11. using Boolean = IPA.Config.Data.Boolean;
  12. namespace IPA.Config.Stores.Converters
  13. {
  14. /// <summary>
  15. /// Provides utility functions for custom converters.
  16. /// </summary>
  17. public static class Converter
  18. {
  19. /// <summary>
  20. /// Gets the integral value of a <see cref="Value"/>, coercing a <see cref="FloatingPoint"/> if necessary,
  21. /// or <see langword="null"/> if <paramref name="val"/> is not an <see cref="Integer"/> or <see cref="FloatingPoint"/>.
  22. /// </summary>
  23. /// <param name="val">the <see cref="Value"/> to get the integral value of</param>
  24. /// <returns>the integral value of <paramref name="val"/>, or <see langword="null"/></returns>
  25. public static long? IntValue(Value? val)
  26. => val is Integer inte ? inte.Value :
  27. val is FloatingPoint fp ? fp.AsInteger()?.Value :
  28. null;
  29. /// <summary>
  30. /// Gets the floaing point value of a <see cref="Value"/>, coercing an <see cref="Integer"/> if necessary,
  31. /// or <see langword="null"/> if <paramref name="val"/> is not an <see cref="Integer"/> or <see cref="FloatingPoint"/>.
  32. /// </summary>
  33. /// <param name="val">the <see cref="Value"/> to get the floaing point value of</param>
  34. /// <returns>the floaing point value of <paramref name="val"/>, or <see langword="null"/></returns>
  35. public static decimal? FloatValue(Value? val)
  36. => val is FloatingPoint fp ? fp.Value :
  37. val is Integer inte ? inte.AsFloat()?.Value :
  38. null;
  39. internal static Type GetDefaultConverterType(Type t)
  40. {
  41. if (t.IsEnum)
  42. {
  43. return typeof(CaseInsensitiveEnumConverter<>).MakeGenericType(t);
  44. }
  45. if (t.IsGenericType)
  46. {
  47. var generic = t.GetGenericTypeDefinition();
  48. var args = t.GetGenericArguments();
  49. if (generic == typeof(List<>))
  50. return (typeof(ListConverter<>).MakeGenericType(args));
  51. else if (generic == typeof(IList<>))
  52. return (typeof(IListConverter<>).MakeGenericType(args));
  53. else if (generic == typeof(Dictionary<,>) && args[0] == typeof(string))
  54. return (typeof(DictionaryConverter<>).MakeGenericType(args[1]));
  55. else if (generic == typeof(IDictionary<,>) && args[0] == typeof(string))
  56. return (typeof(IDictionaryConverter<>).MakeGenericType(args[1]));
  57. #if NET4
  58. else if (generic == typeof(ISet<>))
  59. return (typeof(ISetConverter<>).MakeGenericType(args));
  60. else if (generic == typeof(IReadOnlyDictionary<,>) && args[0] == typeof(string))
  61. return (typeof(IReadOnlyDictionaryConverter<>).MakeGenericType(args[1]));
  62. #endif
  63. }
  64. var iCollBase = t.GetInterfaces()
  65. .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICollection<>));
  66. if (iCollBase != null && t.GetConstructor(Type.EmptyTypes) != null)
  67. { // if it implements ICollection and has a default constructor
  68. var valueType = iCollBase.GetGenericArguments().First();
  69. return (typeof(CollectionConverter<,>).MakeGenericType(valueType, t));
  70. }
  71. if (t == typeof(string))
  72. {
  73. //Logger.log.Debug($"gives StringConverter");
  74. return typeof(StringConverter);
  75. }
  76. if (t.IsValueType)
  77. { // we have to do this garbo to make it accept the thing that we know is a value type at instantiation time
  78. if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>))
  79. { // this is a Nullable
  80. //Logger.log.Debug($"gives NullableConverter<{Nullable.GetUnderlyingType(t)}>");
  81. return (typeof(NullableConverter<>).MakeGenericType(Nullable.GetUnderlyingType(t)));
  82. }
  83. //Logger.log.Debug($"gives converter for value type {t}");
  84. var valConv = (IValConv)Activator.CreateInstance(typeof(ValConv<>).MakeGenericType(t));
  85. return valConv.Get();
  86. }
  87. //Logger.log.Debug($"gives CustomObjectConverter<{t}>");
  88. return (typeof(CustomObjectConverter<>).MakeGenericType(t));
  89. }
  90. internal interface IValConv
  91. {
  92. Type Get();
  93. }
  94. internal interface IValConv<T>
  95. {
  96. Type Get();
  97. }
  98. internal class ValConv<T> : IValConv, IValConv<T> where T : struct
  99. {
  100. private static readonly IValConv<T> Impl = ValConvImpls.Impl as IValConv<T> ?? new ValConv<T>();
  101. public Type Get() => Impl.Get();
  102. Type IValConv<T>.Get()
  103. => typeof(CustomValueTypeConverter<T>);
  104. }
  105. private class ValConvImpls : IValConv<char>,
  106. IValConv<IntPtr>, IValConv<UIntPtr>,
  107. IValConv<long>, IValConv<ulong>,
  108. IValConv<int>, IValConv<uint>,
  109. IValConv<short>, IValConv<ushort>,
  110. IValConv<sbyte>, IValConv<byte>,
  111. IValConv<float>, IValConv<double>,
  112. IValConv<decimal>, IValConv<bool>,
  113. IValConv<DateTime>, IValConv<DateTimeOffset>,
  114. IValConv<TimeSpan>
  115. {
  116. internal static readonly ValConvImpls Impl = new();
  117. Type IValConv<char>.Get() => typeof(CharConverter);
  118. Type IValConv<long>.Get() => typeof(LongConverter);
  119. Type IValConv<ulong>.Get() => typeof(ULongConverter);
  120. Type IValConv<IntPtr>.Get() => typeof(IntPtrConverter);
  121. Type IValConv<UIntPtr>.Get() => typeof(UIntPtrConverter);
  122. Type IValConv<int>.Get() => typeof(IntConverter);
  123. Type IValConv<uint>.Get() => typeof(UIntConverter);
  124. Type IValConv<short>.Get() => typeof(ShortConverter);
  125. Type IValConv<ushort>.Get() => typeof(UShortConverter);
  126. Type IValConv<byte>.Get() => typeof(ByteConverter);
  127. Type IValConv<sbyte>.Get() => typeof(SByteConverter);
  128. Type IValConv<float>.Get() => typeof(FloatConverter);
  129. Type IValConv<double>.Get() => typeof(DoubleConverter);
  130. Type IValConv<decimal>.Get() => typeof(DecimalConverter);
  131. Type IValConv<bool>.Get() => typeof(BooleanConverter);
  132. Type IValConv<DateTime>.Get() => typeof(DateTimeConverter);
  133. Type IValConv<DateTimeOffset>.Get() => typeof(DateTimeOffsetConverter);
  134. Type IValConv<TimeSpan>.Get() => typeof(TimeSpanConverter);
  135. }
  136. }
  137. /// <summary>
  138. /// Provides generic utilities for converters for certain types.
  139. /// </summary>
  140. /// <typeparam name="T">the type of the <see cref="ValueConverter{T}"/> that this works on</typeparam>
  141. public static class Converter<T>
  142. {
  143. private static ValueConverter<T>? defaultConverter;
  144. /// <summary>
  145. /// Gets the default <see cref="ValueConverter{T}"/> for the current type.
  146. /// </summary>
  147. public static ValueConverter<T> Default
  148. => defaultConverter ??= MakeDefault();
  149. internal static ValueConverter<T> MakeDefault()
  150. {
  151. var t = typeof(T);
  152. //Logger.log.Debug($"Converter<{t}>.MakeDefault()");
  153. static ValueConverter<T> MakeInstOf(Type ty)
  154. => (ValueConverter<T>)Activator.CreateInstance(ty);
  155. return MakeInstOf(Converter.GetDefaultConverterType(t));
  156. }
  157. }
  158. /// <summary>
  159. /// A converter for a <see cref="Nullable{T}"/>.
  160. /// </summary>
  161. /// <typeparam name="T">the underlying type of the <see cref="Nullable{T}"/></typeparam>
  162. public class NullableConverter<T> : ValueConverter<T?> where T : struct
  163. {
  164. private readonly ValueConverter<T> baseConverter;
  165. /// <summary>
  166. /// Creates a converter with the default converter for the base type.
  167. /// Equivalent to
  168. /// <code>
  169. /// new NullableConverter(Converter&lt;T&gt;.Default)
  170. /// </code>
  171. /// </summary>
  172. /// <seealso cref="NullableConverter{T}.NullableConverter(ValueConverter{T})"/>
  173. /// <seealso cref="Converter{T}.Default"/>
  174. public NullableConverter() : this(Converter<T>.Default) { }
  175. /// <summary>
  176. /// Creates a converter with the given underlying <see cref="ValueConverter{T}"/>.
  177. /// </summary>
  178. /// <param name="underlying">the undlerlying <see cref="ValueConverter{T}"/> to use</param>
  179. public NullableConverter(ValueConverter<T> underlying)
  180. => baseConverter = underlying;
  181. /// <summary>
  182. /// Converts a <see cref="Value"/> tree to a value.
  183. /// </summary>
  184. /// <param name="value">the <see cref="Value"/> tree to convert</param>
  185. /// <param name="parent">the object which will own the created object</param>
  186. /// <returns>the object represented by <paramref name="value"/></returns>
  187. public override T? FromValue(Value? value, object parent)
  188. => value is null ? null : new T?(baseConverter.FromValue(value, parent));
  189. /// <summary>
  190. /// Converts a nullable <typeparamref name="T"/> to a <see cref="Value"/> tree.
  191. /// </summary>
  192. /// <param name="obj">the value to serialize</param>
  193. /// <param name="parent">the object which owns <paramref name="obj"/></param>
  194. /// <returns>a <see cref="Value"/> tree representing <paramref name="obj"/>.</returns>
  195. public override Value? ToValue(T? obj, object parent)
  196. => obj is null ? null : baseConverter.ToValue(obj.Value, parent);
  197. }
  198. /// <summary>
  199. /// A converter for a <see cref="Nullable{T}"/> that default-constructs a converter of type <typeparamref name="TConverter"/>
  200. /// to use as the underlying converter. Use this in the <see cref="UseConverterAttribute"/>.
  201. /// </summary>
  202. /// <typeparam name="T">the underlying type of the <see cref="Nullable{T}"/></typeparam>
  203. /// <typeparam name="TConverter">the type to use as an underlying converter</typeparam>
  204. /// <seealso cref="NullableConverter{T}"/>
  205. public sealed class NullableConverter<T, TConverter> : NullableConverter<T>
  206. where T : struct
  207. where TConverter : ValueConverter<T>, new()
  208. {
  209. /// <summary>
  210. /// Creates a converter with a new <typeparamref name="TConverter"/> as the underlying converter.
  211. /// </summary>
  212. /// <seealso cref="NullableConverter{T}.NullableConverter(ValueConverter{T})"/>
  213. public NullableConverter() : base(new TConverter()) { }
  214. }
  215. /// <summary>
  216. /// A converter for an enum of type <typeparamref name="T"/>, that converts the enum to its string representation and back.
  217. /// </summary>
  218. /// <typeparam name="T">the enum type</typeparam>
  219. public sealed class EnumConverter<T> : ValueConverter<T>
  220. where T : Enum
  221. {
  222. /// <summary>
  223. /// Converts a <see cref="Value"/> that is a <see cref="Text"/> node to the corresponding enum value.
  224. /// </summary>
  225. /// <param name="value">the <see cref="Value"/> to convert</param>
  226. /// <param name="parent">the object which will own the created object</param>
  227. /// <returns>the deserialized enum value</returns>
  228. /// <exception cref="ArgumentException">if <paramref name="value"/> is not a <see cref="Text"/> node</exception>
  229. public override T FromValue(Value? value, object parent)
  230. => value is Text t
  231. ? (T)Enum.Parse(typeof(T), t.Value)
  232. : throw new ArgumentException("Value not a string", nameof(value));
  233. /// <summary>
  234. /// Converts an enum of type <typeparamref name="T"/> to a <see cref="Value"/> node corresponding to its value.
  235. /// </summary>
  236. /// <param name="obj">the value to serialize</param>
  237. /// <param name="parent">the object which owns <paramref name="obj"/></param>
  238. /// <returns>a <see cref="Text"/> node representing <paramref name="obj"/></returns>
  239. public override Value? ToValue(T? obj, object parent)
  240. => Value.Text(obj?.ToString());
  241. }
  242. /// <summary>
  243. /// A converter for an enum of type <typeparamref name="T"/>, that converts the enum to its string representation and back,
  244. /// ignoring the case of the serialized value for deseiralization.
  245. /// </summary>
  246. /// <typeparam name="T">the enum type</typeparam>
  247. public sealed class CaseInsensitiveEnumConverter<T> : ValueConverter<T>
  248. where T : Enum
  249. {
  250. /// <summary>
  251. /// Converts a <see cref="Value"/> that is a <see cref="Text"/> node to the corresponding enum value.
  252. /// </summary>
  253. /// <param name="value">the <see cref="Value"/> to convert</param>
  254. /// <param name="parent">the object which will own the created object</param>
  255. /// <returns>the deserialized enum value</returns>
  256. /// <exception cref="ArgumentException">if <paramref name="value"/> is not a <see cref="Text"/> node</exception>
  257. public override T FromValue(Value? value, object parent)
  258. => value is Text t
  259. ? (T)Enum.Parse(typeof(T), t.Value, true)
  260. : throw new ArgumentException("Value not a string", nameof(value));
  261. /// <summary>
  262. /// Converts an enum of type <typeparamref name="T"/> to a <see cref="Value"/> node corresponding to its value.
  263. /// </summary>
  264. /// <param name="obj">the value to serialize</param>
  265. /// <param name="parent">the object which owns <paramref name="obj"/></param>
  266. /// <returns>a <see cref="Text"/> node representing <paramref name="obj"/></returns>
  267. public override Value? ToValue(T? obj, object parent)
  268. => Value.Text(obj?.ToString());
  269. }
  270. /// <summary>
  271. /// A converter for an enum of type <typeparamref name="T"/>, that converts the enum to its underlying value for serialization.
  272. /// </summary>
  273. /// <typeparam name="T">the enum type</typeparam>
  274. public sealed class NumericEnumConverter<T> : ValueConverter<T>
  275. where T : Enum
  276. {
  277. /// <summary>
  278. /// Converts a <see cref="Value"/> that is a numeric node to the corresponding enum value.
  279. /// </summary>
  280. /// <param name="value">the <see cref="Value"/> to convert</param>
  281. /// <param name="parent">the object which will own the created object</param>
  282. /// <returns>the deserialized enum value</returns>
  283. /// <exception cref="ArgumentException">if <paramref name="value"/> is not a numeric node</exception>
  284. public override T FromValue(Value? value, object parent)
  285. => (T)Enum.ToObject(typeof(T), Converter.IntValue(value)
  286. ?? throw new ArgumentException("Value not a numeric node", nameof(value)));
  287. /// <summary>
  288. /// Converts an enum of type <typeparamref name="T"/> to a <see cref="Value"/> node corresponding to its value.
  289. /// </summary>
  290. /// <param name="obj">the value to serialize</param>
  291. /// <param name="parent">the object which owns <paramref name="obj"/></param>
  292. /// <returns>an <see cref="Integer"/> node representing <paramref name="obj"/></returns>
  293. public override Value ToValue(T? obj, object parent)
  294. => Value.Integer(Convert.ToInt64(obj));
  295. }
  296. /// <summary>
  297. /// A converter for instances of <see cref="IDictionary{TKey, TValue}"/>.
  298. /// </summary>
  299. /// <typeparam name="TValue">the value type of the dictionary</typeparam>
  300. public class IDictionaryConverter<TValue> : ValueConverter<IDictionary<string, TValue?>>
  301. {
  302. /// <summary>
  303. /// Gets the converter for the dictionary's value type.
  304. /// </summary>
  305. protected ValueConverter<TValue> BaseConverter { get; }
  306. /// <summary>
  307. /// Constructs an <see cref="IDictionaryConverter{TValue}"/> using the default converter for the value type.
  308. /// </summary>
  309. public IDictionaryConverter() : this(Converter<TValue>.Default) { }
  310. /// <summary>
  311. /// Constructs an <see cref="IDictionaryConverter{TValue}"/> using the specified converter for the value.
  312. /// </summary>
  313. /// <param name="converter">the converter for the value</param>
  314. public IDictionaryConverter(ValueConverter<TValue> converter)
  315. => BaseConverter = converter;
  316. /// <summary>
  317. /// Converts a <see cref="Map"/> to an <see cref="IDictionary{TKey, TValue}"/> that is represented by it.
  318. /// </summary>
  319. /// <param name="value">the <see cref="Map"/> to convert</param>
  320. /// <param name="parent">the parent that will own the resulting object</param>
  321. /// <returns>the deserialized dictionary</returns>
  322. public override IDictionary<string, TValue?> FromValue(Value? value, object parent)
  323. => ((value as Map)?.Select(kvp => (kvp.Key, val: BaseConverter.FromValue(kvp.Value, parent)))
  324. ?.ToDictionary(p => p.Key, p => p.val))
  325. ?? throw new ArgumentException("Value not a map", nameof(value));
  326. /// <summary>
  327. /// Serializes an <see cref="IDictionary{TKey, TValue}"/> into a <see cref="Map"/> containing its values.
  328. /// </summary>
  329. /// <param name="obj">the dictionary to serialize</param>
  330. /// <param name="parent">the object that owns the dictionary</param>
  331. /// <returns>the dictionary serialized as a <see cref="Map"/></returns>
  332. public override Value? ToValue(IDictionary<string, TValue?>? obj, object parent)
  333. => Value.From(obj.Select(p => new KeyValuePair<string, Value?>(p.Key, BaseConverter.ToValue(p.Value, parent))));
  334. }
  335. /// <summary>
  336. /// A converter for instances of <see cref="IDictionary{TKey, TValue}"/>, specifying a value converter as a type parameter.
  337. /// </summary>
  338. /// <typeparam name="TValue">the value type of the dictionary</typeparam>
  339. /// <typeparam name="TConverter">the converter type for values</typeparam>
  340. public sealed class IDictionaryConverter<TValue, TConverter> : IDictionaryConverter<TValue>
  341. where TConverter : ValueConverter<TValue>, new()
  342. {
  343. /// <summary>
  344. /// Constructs a new <see cref="IDictionaryConverter{TValue, TConverter}"/> with a new instance of
  345. /// <typeparamref name="TConverter"/> as the value converter.
  346. /// </summary>
  347. public IDictionaryConverter() : base(new TConverter()) { }
  348. }
  349. /// <summary>
  350. /// A converter for instances of <see cref="Dictionary{TKey, TValue}"/>.
  351. /// </summary>
  352. /// <typeparam name="TValue">the value type of the dictionary</typeparam>
  353. public class DictionaryConverter<TValue> : ValueConverter<Dictionary<string, TValue?>>
  354. {
  355. /// <summary>
  356. /// Gets the converter for the dictionary's value type.
  357. /// </summary>
  358. protected ValueConverter<TValue> BaseConverter { get; }
  359. /// <summary>
  360. /// Constructs an <see cref="IDictionaryConverter{TValue}"/> using the default converter for the value type.
  361. /// </summary>
  362. public DictionaryConverter() : this(Converter<TValue>.Default) { }
  363. /// <summary>
  364. /// Constructs an <see cref="IDictionaryConverter{TValue}"/> using the specified converter for the value.
  365. /// </summary>
  366. /// <param name="converter">the converter for the value</param>
  367. public DictionaryConverter(ValueConverter<TValue> converter)
  368. => BaseConverter = converter;
  369. /// <summary>
  370. /// Converts a <see cref="Map"/> to a <see cref="Dictionary{TKey, TValue}"/> that is represented by it.
  371. /// </summary>
  372. /// <param name="value">the <see cref="Map"/> to convert</param>
  373. /// <param name="parent">the parent that will own the resulting object</param>
  374. /// <returns>the deserialized dictionary</returns>
  375. public override Dictionary<string, TValue?> FromValue(Value? value, object parent)
  376. => (value as Map)?.Select(kvp => (kvp.Key, val: BaseConverter.FromValue(kvp.Value, parent)))
  377. ?.ToDictionary(p => p.Key, p => p.val)
  378. ?? throw new ArgumentException("Value not a map", nameof(value));
  379. /// <summary>
  380. /// Serializes a <see cref="Dictionary{TKey, TValue}"/> into a <see cref="Map"/> containing its values.
  381. /// </summary>
  382. /// <param name="obj">the dictionary to serialize</param>
  383. /// <param name="parent">the object that owns the dictionary</param>
  384. /// <returns>the dictionary serialized as a <see cref="Map"/></returns>
  385. public override Value? ToValue(Dictionary<string, TValue?>? obj, object parent)
  386. => Value.From(obj?.Select(p => new KeyValuePair<string, Value?>(p.Key, BaseConverter.ToValue(p.Value, parent))));
  387. }
  388. /// <summary>
  389. /// A converter for instances of <see cref="Dictionary{TKey, TValue}"/>, specifying a value converter as a type parameter.
  390. /// </summary>
  391. /// <typeparam name="TValue">the value type of the dictionary</typeparam>
  392. /// <typeparam name="TConverter">the converter type for values</typeparam>
  393. public sealed class DictionaryConverter<TValue, TConverter> : DictionaryConverter<TValue>
  394. where TConverter : ValueConverter<TValue>, new()
  395. {
  396. /// <summary>
  397. /// Constructs a new <see cref="IDictionaryConverter{TValue, TConverter}"/> with a new instance of
  398. /// <typeparamref name="TConverter"/> as the value converter.
  399. /// </summary>
  400. public DictionaryConverter() : base(new TConverter()) { }
  401. }
  402. #if NET4
  403. /// <summary>
  404. /// A converter for instances of <see cref="IReadOnlyDictionary{TKey, TValue}"/>.
  405. /// </summary>
  406. /// <typeparam name="TValue">the value type of the dictionary</typeparam>
  407. public class IReadOnlyDictionaryConverter<TValue> : ValueConverter<IReadOnlyDictionary<string, TValue?>>
  408. {
  409. /// <summary>
  410. /// Gets the converter for the dictionary's value type.
  411. /// </summary>
  412. protected ValueConverter<TValue> BaseConverter { get; }
  413. /// <summary>
  414. /// Constructs an <see cref="IReadOnlyDictionaryConverter{TValue}"/> using the default converter for the value type.
  415. /// </summary>
  416. public IReadOnlyDictionaryConverter() : this(Converter<TValue>.Default) { }
  417. /// <summary>
  418. /// Constructs an <see cref="IReadOnlyDictionaryConverter{TValue}"/> using the specified converter for the value.
  419. /// </summary>
  420. /// <param name="converter">the converter for the value</param>
  421. public IReadOnlyDictionaryConverter(ValueConverter<TValue> converter)
  422. => BaseConverter = converter;
  423. /// <summary>
  424. /// Converts a <see cref="Map"/> to an <see cref="IDictionary{TKey, TValue}"/> that is represented by it.
  425. /// </summary>
  426. /// <param name="value">the <see cref="Map"/> to convert</param>
  427. /// <param name="parent">the parent that will own the resulting object</param>
  428. /// <returns>the deserialized dictionary</returns>
  429. public override IReadOnlyDictionary<string, TValue?> FromValue(Value? value, object parent)
  430. => (value as Map)?.Select(kvp => (kvp.Key, val: BaseConverter.FromValue(kvp.Value, parent)))
  431. ?.ToDictionary(p => p.Key, p => p.val)
  432. ?? throw new ArgumentException("Value not a map", nameof(value));
  433. /// <summary>
  434. /// Serializes an <see cref="IDictionary{TKey, TValue}"/> into a <see cref="Map"/> containing its values.
  435. /// </summary>
  436. /// <param name="obj">the dictionary to serialize</param>
  437. /// <param name="parent">the object that owns the dictionary</param>
  438. /// <returns>the dictionary serialized as a <see cref="Map"/></returns>
  439. public override Value? ToValue(IReadOnlyDictionary<string, TValue?>? obj, object parent)
  440. => Value.From(obj?.Select(p => new KeyValuePair<string, Value?>(p.Key, BaseConverter.ToValue(p.Value, parent))));
  441. }
  442. /// <summary>
  443. /// A converter for instances of <see cref="IReadOnlyDictionary{TKey, TValue}"/>, specifying a value converter as a type parameter.
  444. /// </summary>
  445. /// <typeparam name="TValue">the value type of the dictionary</typeparam>
  446. /// <typeparam name="TConverter">the converter type for values</typeparam>
  447. public sealed class IReadOnlyDictionaryConverter<TValue, TConverter> : IReadOnlyDictionaryConverter<TValue>
  448. where TConverter : ValueConverter<TValue>, new()
  449. {
  450. /// <summary>
  451. /// Constructs a new <see cref="IReadOnlyDictionaryConverter{TValue, TConverter}"/> with a new instance of
  452. /// <typeparamref name="TConverter"/> as the value converter.
  453. /// </summary>
  454. public IReadOnlyDictionaryConverter() : base(new TConverter()) { }
  455. }
  456. #endif
  457. /// <summary>
  458. /// A converter for <see cref="Color"/> objects.
  459. /// </summary>
  460. public sealed class HexColorConverter : ValueConverter<Color>
  461. {
  462. /// <summary>
  463. /// Converts a <see cref="Value"/> that is a <see cref="Text"/> node to the corresponding <see cref="Color" /> object.
  464. /// </summary>
  465. /// <param name="value">the <see cref="Value"/> to convert</param>
  466. /// <param name="parent">the object which will own the created object</param>
  467. /// <returns>the deserialized Color object</returns>
  468. /// <exception cref="ArgumentException">if <paramref name="value"/> is not a <see cref="Text"/> node or couldn't be parsed into a Color object</exception>
  469. public override Color FromValue(Value? value, object parent)
  470. {
  471. if (value is Text t)
  472. {
  473. if (ColorUtility.TryParseHtmlString(t.Value, out Color color))
  474. {
  475. return color;
  476. }
  477. throw new ArgumentException("Value cannot be parsed into a Color.", nameof(value));
  478. }
  479. throw new ArgumentException("Value not a string", nameof(value));
  480. }
  481. /// <summary>
  482. /// Converts color of type <see cref="Color"/> to a <see cref="Value"/> node.
  483. /// </summary>
  484. /// <param name="obj">the object to serialize</param>
  485. /// <param name="parent">the object which owns <paramref name="obj"/></param>
  486. /// <returns>a <see cref="Text"/> node representing <paramref name="obj"/></returns>
  487. public override Value ToValue(Color obj, object parent) => Value.Text($"#{ColorUtility.ToHtmlStringRGB(obj)}");
  488. }
  489. internal class StringConverter : ValueConverter<string>
  490. {
  491. public override string? FromValue(Value? value, object parent)
  492. => (value as Text)?.Value;
  493. public override Value? ToValue(string? obj, object parent)
  494. => Value.From(obj);
  495. }
  496. internal class CharConverter : ValueConverter<char>
  497. {
  498. public override char FromValue(Value? value, object parent)
  499. => (value as Text)?.Value[0]
  500. ?? throw new ArgumentException("Value not a text node", nameof(value)); // can throw nullptr
  501. public override Value? ToValue(char obj, object parent)
  502. => Value.From(char.ToString(obj));
  503. }
  504. internal class LongConverter : ValueConverter<long>
  505. {
  506. public override long FromValue(Value? value, object parent)
  507. => Converter.IntValue(value)
  508. ?? throw new ArgumentException("Value not a numeric value", nameof(value));
  509. public override Value? ToValue(long obj, object parent)
  510. => Value.From(obj);
  511. }
  512. internal class ULongConverter : ValueConverter<ulong>
  513. {
  514. public override ulong FromValue(Value? value, object parent)
  515. => (ulong)(Converter.FloatValue(value)
  516. ?? throw new ArgumentException("Value not a numeric value", nameof(value)));
  517. public override Value? ToValue(ulong obj, object parent)
  518. => Value.From(obj);
  519. }
  520. internal class IntPtrConverter : ValueConverter<IntPtr>
  521. {
  522. public override IntPtr FromValue(Value? value, object parent)
  523. => (IntPtr)Converter<long>.Default.FromValue(value, parent);
  524. public override Value? ToValue(IntPtr obj, object parent)
  525. => Value.From((long)obj);
  526. }
  527. internal class UIntPtrConverter : ValueConverter<UIntPtr>
  528. {
  529. public override UIntPtr FromValue(Value? value, object parent)
  530. => (UIntPtr)Converter<ulong>.Default.FromValue(value, parent);
  531. public override Value? ToValue(UIntPtr obj, object parent)
  532. => Value.From((decimal)obj);
  533. }
  534. internal class IntConverter : ValueConverter<int>
  535. {
  536. public override int FromValue(Value? value, object parent)
  537. => (int)Converter<long>.Default.FromValue(value, parent);
  538. public override Value? ToValue(int obj, object parent)
  539. => Value.From(obj);
  540. }
  541. internal class UIntConverter : ValueConverter<uint>
  542. {
  543. public override uint FromValue(Value? value, object parent)
  544. => (uint)Converter<long>.Default.FromValue(value, parent);
  545. public override Value? ToValue(uint obj, object parent)
  546. => Value.From(obj);
  547. }
  548. internal class ShortConverter : ValueConverter<short>
  549. {
  550. public override short FromValue(Value? value, object parent)
  551. => (short)Converter<long>.Default.FromValue(value, parent);
  552. public override Value? ToValue(short obj, object parent)
  553. => Value.From(obj);
  554. }
  555. internal class UShortConverter : ValueConverter<ushort>
  556. {
  557. public override ushort FromValue(Value? value, object parent)
  558. => (ushort)Converter<long>.Default.FromValue(value, parent);
  559. public override Value? ToValue(ushort obj, object parent)
  560. => Value.From(obj);
  561. }
  562. internal class ByteConverter : ValueConverter<byte>
  563. {
  564. public override byte FromValue(Value? value, object parent)
  565. => (byte)Converter<long>.Default.FromValue(value, parent);
  566. public override Value? ToValue(byte obj, object parent)
  567. => Value.From(obj);
  568. }
  569. internal class SByteConverter : ValueConverter<sbyte>
  570. {
  571. public override sbyte FromValue(Value? value, object parent)
  572. => (sbyte)Converter<long>.Default.FromValue(value, parent);
  573. public override Value? ToValue(sbyte obj, object parent)
  574. => Value.From(obj);
  575. }
  576. internal class DecimalConverter : ValueConverter<decimal>
  577. {
  578. public override decimal FromValue(Value? value, object parent)
  579. => Converter.FloatValue(value) ?? throw new ArgumentException("Value not a numeric value", nameof(value));
  580. public override Value? ToValue(decimal obj, object parent)
  581. => Value.From(obj);
  582. }
  583. internal class FloatConverter : ValueConverter<float>
  584. {
  585. public override float FromValue(Value? value, object parent)
  586. => (float)Converter<decimal>.Default.FromValue(value, parent);
  587. public override Value? ToValue(float obj, object parent)
  588. => Value.From((decimal)obj);
  589. }
  590. internal class DoubleConverter : ValueConverter<double>
  591. {
  592. public override double FromValue(Value? value, object parent)
  593. => (double)Converter<decimal>.Default.FromValue(value, parent);
  594. public override Value? ToValue(double obj, object parent)
  595. => Value.From((decimal)obj);
  596. }
  597. internal class BooleanConverter : ValueConverter<bool>
  598. {
  599. public override bool FromValue(Value? value, object parent)
  600. => (value as Boolean)?.Value ?? throw new ArgumentException("Value not a Boolean", nameof(value));
  601. public override Value? ToValue(bool obj, object parent)
  602. => Value.From(obj);
  603. }
  604. internal class DateTimeConverter : ValueConverter<DateTime>
  605. {
  606. public override DateTime FromValue(Value? value, object parent)
  607. {
  608. if (value is not Text text)
  609. {
  610. throw new ArgumentException("Value is not of type Text", nameof(value));
  611. }
  612. if (DateTime.TryParse(text.Value, out var dateTime))
  613. {
  614. return dateTime;
  615. }
  616. throw new ArgumentException($"Parsing failed, {text.Value}");
  617. }
  618. public override Value? ToValue(DateTime obj, object parent) => Value.Text(obj.ToString("O"));
  619. }
  620. internal class DateTimeOffsetConverter : ValueConverter<DateTimeOffset>
  621. {
  622. public override DateTimeOffset FromValue(Value? value, object parent)
  623. {
  624. if (value is not Text text)
  625. {
  626. throw new ArgumentException("Value is not of type Text", nameof(value));
  627. }
  628. if (DateTimeOffset.TryParse(text.Value, out var dateTime))
  629. {
  630. return dateTime;
  631. }
  632. throw new ArgumentException($"Parsing failed, {text.Value}");
  633. }
  634. public override Value ToValue(DateTimeOffset obj, object parent) => Value.Text(obj.ToString("O"));
  635. }
  636. internal class TimeSpanConverter : ValueConverter<TimeSpan>
  637. {
  638. public override TimeSpan FromValue(Value? value, object parent)
  639. {
  640. if (value is not Text text)
  641. {
  642. throw new ArgumentException("Value is not of type Text", nameof(value));
  643. }
  644. if (TimeSpan.TryParse(text.Value, out var dateTime))
  645. {
  646. return dateTime;
  647. }
  648. throw new ArgumentException($"Parsing failed, {text.Value}");
  649. }
  650. public override Value? ToValue(TimeSpan obj, object parent) => Value.Text(obj.ToString());
  651. }
  652. }