#nullable enable using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; namespace Barotrauma { // Identifier struct to eliminate case-sensitive comparisons public readonly struct Identifier : IComparable, IEquatable { public readonly static Identifier Empty = default; private readonly static int emptyHash = "".GetHashCode(StringComparison.OrdinalIgnoreCase); private readonly string? value; private readonly Lazy? hashCode; public string Value => value ?? ""; public int HashCode => hashCode?.Value ?? emptyHash; public Identifier(string? str) { value = str; hashCode = new Lazy(() => (str ?? "").GetHashCode(StringComparison.OrdinalIgnoreCase)); } public bool IsEmpty => Value.IsNullOrEmpty(); public Identifier IfEmpty(in Identifier id) => IsEmpty ? id : this; public Identifier Replace(in Identifier subStr, in Identifier newStr) => Replace(subStr.Value, newStr.Value); public Identifier Replace(string subStr, string newStr) => Value.Replace(subStr, newStr, StringComparison.OrdinalIgnoreCase).ToIdentifier(); public Identifier Remove(Identifier subStr) => Remove(subStr.Value); public Identifier Remove(string subStr) => Value.Remove(subStr, StringComparison.OrdinalIgnoreCase).ToIdentifier(); public override bool Equals(object? obj) => obj switch { Identifier i => this == i, string s => this == s, _ => base.Equals(obj) }; public bool StartsWith(string str) => Value.StartsWith(str, StringComparison.OrdinalIgnoreCase); public bool StartsWith(Identifier id) => StartsWith(id.Value); public bool EndsWith(string str) => Value.EndsWith(str, StringComparison.OrdinalIgnoreCase); public bool EndsWith(Identifier id) => EndsWith(id.Value); public Identifier AppendIfMissing(string suffix) => EndsWith(suffix) ? this : $"{this}{suffix}".ToIdentifier(); public Identifier RemoveFromEnd(string suffix) => Value.RemoveFromEnd(suffix, StringComparison.OrdinalIgnoreCase).ToIdentifier(); public bool Contains(string str) => Value.Contains(str, StringComparison.OrdinalIgnoreCase); public bool Contains(in Identifier id) => Contains(id.Value); public override string ToString() => Value; public override int GetHashCode() => HashCode; public int CompareTo(object? obj) { return string.Compare(Value, obj?.ToString() ?? "", StringComparison.InvariantCultureIgnoreCase); } public bool Equals([AllowNull] Identifier other) { return this == other; } private static bool StringEquality(string? a, string? b) => (a.IsNullOrEmpty() && b.IsNullOrEmpty()) || string.Equals(a, b, StringComparison.OrdinalIgnoreCase); public static bool operator ==(in Identifier a, in Identifier b) => StringEquality(a.Value, b.Value); public static bool operator !=(in Identifier a, in Identifier b) => !(a == b); public static bool operator ==(in Identifier identifier, string? str) => StringEquality(identifier.Value, str); public static bool operator !=(in Identifier identifier, string? str) => !(identifier == str); public static bool operator ==(string? str, in Identifier identifier) => identifier == str; public static bool operator !=(string? str, in Identifier identifier) => !(identifier == str); public static bool operator ==(in Identifier? a, in Identifier? b) => StringEquality(a?.Value, b?.Value); public static bool operator !=(in Identifier? a, in Identifier? b) => !(a == b); public static bool operator ==(in Identifier? a, string? b) => StringEquality(a?.Value, b); public static bool operator !=(in Identifier? a, string? b) => !(a == b); public static bool operator ==(string str, in Identifier? identifier) => identifier == str; public static bool operator !=(string str, in Identifier? identifier) => !(identifier == str); public int IndexOf(char c) => Value.IndexOf(c); public Identifier this[Range range] => Value[range].ToIdentifier(); public Char this[int i] => Value[i]; } public static class IdentifierExtensions { public static IEnumerable ToIdentifiers(this IEnumerable strings) { foreach (string s in strings) { if (string.IsNullOrEmpty(s)) { continue; } yield return new Identifier(s); } } public static Identifier[] ToIdentifiers(this string[] strings) => ((IEnumerable)strings).ToIdentifiers().ToArray(); public static Identifier ToIdentifier(this string? s) { return new Identifier(s); } public static Identifier ToIdentifier(this T t) where T: notnull { return t.ToString().ToIdentifier(); } public static bool Contains(this ISet set, string identifier) { return set.Contains(identifier.ToIdentifier()); } public static bool ContainsKey(this IReadOnlyDictionary dictionary, string key) { return dictionary.ContainsKey(key.ToIdentifier()); } } }