89 lines
3.3 KiB
C#
89 lines
3.3 KiB
C#
using System.Collections.Concurrent;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
|
|
namespace PkmnLib.Static.Utils;
|
|
|
|
/// <summary>
|
|
/// A case-insensitive string key. We use this class for things like looking up data from dictionaries, etc.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This is a struct, as it's effectively just a wrapper around a single reference object. Heap allocation would be silly.
|
|
/// </remarks>
|
|
public readonly struct StringKey : IEquatable<StringKey>, IEquatable<string>
|
|
{
|
|
private static readonly ConcurrentDictionary<string, int> HashCodes = new();
|
|
|
|
private readonly string _key;
|
|
private readonly int _hashCode;
|
|
|
|
/// <inheritdoc cref="StringKey"/>
|
|
public StringKey(string key)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(key))
|
|
throw new ArgumentException("Key cannot be null or whitespace.", nameof(key));
|
|
_key = key;
|
|
if (!HashCodes.TryGetValue(_key, out _hashCode))
|
|
{
|
|
_hashCode = HashCodes[_key] = StringComparer.InvariantCultureIgnoreCase.GetHashCode(_key);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts a <see cref="StringKey"/> to a <see cref="string"/>.
|
|
/// </summary>
|
|
public static implicit operator string(StringKey key) => key._key;
|
|
|
|
/// <summary>
|
|
/// Converts a <see cref="string"/> to a <see cref="StringKey"/>.
|
|
/// </summary>
|
|
[return: NotNullIfNotNull("key")]
|
|
public static implicit operator StringKey?(string? key) =>
|
|
string.IsNullOrWhiteSpace(key) ? null! : new StringKey(key);
|
|
|
|
/// <summary>
|
|
/// Converts a <see cref="string"/> to a <see cref="StringKey"/>.
|
|
/// Throws an <see cref="ArgumentException"/> if the key is null or whitespace.
|
|
/// </summary>
|
|
public static implicit operator StringKey(string key) =>
|
|
string.IsNullOrWhiteSpace(key)
|
|
? throw new ArgumentException("Key cannot be null or whitespace.", nameof(key))
|
|
: new StringKey(key);
|
|
|
|
/// <inheritdoc cref="string.ToString()"/>
|
|
public override string ToString() => _key.ToLowerInvariant();
|
|
|
|
/// <inheritdoc />
|
|
public override bool Equals(object? obj)
|
|
{
|
|
return obj switch
|
|
{
|
|
StringKey other => Equals(other),
|
|
string str => Equals(str),
|
|
_ => false,
|
|
};
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public bool Equals(StringKey other) => _hashCode == other._hashCode;
|
|
|
|
/// <inheritdoc cref="Equals(StringKey)"/>
|
|
public bool Equals(string other) => string.Equals(_key, other, StringComparison.InvariantCultureIgnoreCase);
|
|
|
|
/// <inheritdoc />
|
|
public override int GetHashCode() => _hashCode;
|
|
|
|
/// <inheritdoc cref="Equals(StringKey)"/>
|
|
public static bool operator ==(StringKey? left, string? right) =>
|
|
(left is null && right is null) || (right != null && (left?.Equals(right) ?? false));
|
|
|
|
/// <inheritdoc cref="Equals(StringKey)"/>
|
|
public static bool operator !=(StringKey? left, string? right) => !(left == right);
|
|
|
|
/// <inheritdoc cref="Equals(StringKey)"/>
|
|
public static bool operator ==(StringKey? left, StringKey right) => left?.Equals(right) ?? false;
|
|
|
|
/// <inheritdoc cref="Equals(StringKey)"/>
|
|
public static bool operator !=(StringKey? left, StringKey right) => !(left == right);
|
|
|
|
public bool Contains(StringKey other) => _key.IndexOf(other._key, StringComparison.InvariantCultureIgnoreCase) >= 0;
|
|
} |