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.

134 lines
5.8 KiB

  1. #nullable enable
  2. using IPA.Utilities;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. namespace IPA.Config.Data
  7. {
  8. /// <summary>
  9. /// A ordered map of <see cref="string"/> to <see cref="Value"/> for serialization by an <see cref="IConfigProvider"/>.
  10. /// Use <see cref="Value.Map"/> or <see cref="Value.From(IDictionary{string, Value})"/> to create.
  11. /// </summary>
  12. public sealed class Map : Value, IDictionary<string, Value?>
  13. {
  14. private readonly Dictionary<string, Value?> values = new();
  15. private readonly List<string> keyOrder = new();
  16. internal Map() { }
  17. /// <summary>
  18. /// Accesses the <see cref="Value"/> at <paramref name="key"/> in the map.
  19. /// </summary>
  20. /// <param name="key">the key to get the value associated with</param>
  21. /// <returns>the value associated with the <paramref name="key"/></returns>
  22. /// <seealso cref="IDictionary{TKey, TValue}.this[TKey]"/>
  23. public Value? this[string key] { get => values[key]; set => values[key] = value; }
  24. /// <summary>
  25. /// Gets a collection of the keys for the <see cref="Map"/>.
  26. /// </summary>
  27. /// <seealso cref="IDictionary{TKey, TValue}.Keys"/>
  28. public ICollection<string> Keys => keyOrder;
  29. /// <summary>
  30. /// Gets a collection of the values in the <see cref="Map"/>.
  31. /// </summary>
  32. /// <remarks>
  33. /// Unlike all other iterables given by <see cref="Map"/>, this does <i>not</i>
  34. /// guarantee that order is maintained.
  35. /// </remarks>
  36. /// <seealso cref="IDictionary{TKey, TValue}.Values"/>
  37. public ICollection<Value?> Values => values.Values;
  38. /// <summary>
  39. /// Gets the number of key-value pairs in this <see cref="Map"/>.
  40. /// </summary>
  41. /// <seealso cref="ICollection{T}.Count"/>
  42. public int Count => values.Count;
  43. bool ICollection<KeyValuePair<string, Value?>>.IsReadOnly => ((IDictionary<string, Value?>)values).IsReadOnly;
  44. /// <summary>
  45. /// Adds a new <see cref="Value"/> with a given key.
  46. /// </summary>
  47. /// <param name="key">the key to put the value at</param>
  48. /// <param name="value">the <see cref="Value"/> to add</param>
  49. /// <seealso cref="IDictionary{TKey, TValue}.Add(TKey, TValue)"/>
  50. public void Add(string key, Value? value)
  51. {
  52. values.Add(key, value);
  53. keyOrder.Add(key);
  54. }
  55. void ICollection<KeyValuePair<string, Value?>>.Add(KeyValuePair<string, Value?> item)
  56. => Add(item.Key, item.Value);
  57. /// <summary>
  58. /// Clears the <see cref="Map"/> of its key-value pairs.
  59. /// </summary>
  60. /// <seealso cref="ICollection{T}.Clear"/>
  61. public void Clear()
  62. {
  63. values.Clear();
  64. keyOrder.Clear();
  65. }
  66. bool ICollection<KeyValuePair<string, Value?>>.Contains(KeyValuePair<string, Value?> item)
  67. => ((IDictionary<string, Value?>)values).Contains(item);
  68. /// <summary>
  69. /// Checks if the <see cref="Map"/> contains a given <paramref name="key"/>.
  70. /// </summary>
  71. /// <param name="key">the key to check for</param>
  72. /// <returns><see langword="true"/> if the key exists, otherwise <see langword="false"/></returns>
  73. /// <seealso cref="IDictionary{TKey, TValue}.ContainsKey(TKey)"/>
  74. public bool ContainsKey(string key) => values.ContainsKey(key);
  75. void ICollection<KeyValuePair<string, Value?>>.CopyTo(KeyValuePair<string, Value?>[] array, int arrayIndex)
  76. => ((IDictionary<string, Value?>)values).CopyTo(array, arrayIndex);
  77. /// <summary>
  78. /// Enumerates the <see cref="Map"/>'s key-value pairs.
  79. /// </summary>
  80. /// <returns>an <see cref="IEnumerator{T}"/> of key-value pairs in this <see cref="Map"/></returns>
  81. /// <seealso cref="IEnumerable{T}.GetEnumerator()"/>
  82. public IEnumerator<KeyValuePair<string, Value?>> GetEnumerator()
  83. {
  84. foreach (var key in keyOrder)
  85. yield return new KeyValuePair<string, Value?>(key, this[key]);
  86. }
  87. /// <summary>
  88. /// Removes the object associated with a key in this <see cref="Map"/>.
  89. /// </summary>
  90. /// <param name="key">the key to remove</param>
  91. /// <returns><see langword="true"/> if the key existed, <see langword="false"/> otherwise</returns>
  92. /// <seealso cref="IDictionary{TKey, TValue}.Remove(TKey)"/>
  93. public bool Remove(string key) => values.Remove(key) && keyOrder.Remove(key);
  94. bool ICollection<KeyValuePair<string, Value?>>.Remove(KeyValuePair<string, Value?> item)
  95. => ((IDictionary<string, Value?>)values).Remove(item) && (keyOrder.Remove(item.Key) || true);
  96. /// <summary>
  97. /// Gets the value associated with the specified key.
  98. /// </summary>
  99. /// <param name="key">the key of the value to get</param>
  100. /// <param name="value">the target location of the retrieved object</param>
  101. /// <returns><see langword="true"/> if the key was found and <paramref name="value"/> set, <see langword="false"/> otherwise</returns>
  102. /// <seealso cref="IDictionary{TKey, TValue}.TryGetValue(TKey, out TValue)"/>
  103. public bool TryGetValue(string key, out Value? value) => values.TryGetValue(key, out value);
  104. IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
  105. /// <summary>
  106. /// Converts this <see cref="Value"/> into a human-readable format.
  107. /// </summary>
  108. /// <returns>a JSON-like set of key-value pairs</returns>
  109. public override string ToString()
  110. => $"{{{string.Join(",", this.Select(p => $"\"{p.Key}\":{p.Value?.ToString() ?? "null"}").StrJP())}}}";
  111. }
  112. }