#nullable enable using System; using System.Diagnostics.CodeAnalysis; namespace Barotrauma { public readonly struct Option where T : notnull { private readonly bool hasValue; private readonly T? value; private Option(bool hasValue, T? value) { this.hasValue = hasValue; this.value = value; } public bool IsSome() => hasValue; public bool IsNone() => !IsSome(); public bool TryUnwrap([NotNullWhen(returnValue: true)] out T1? outValue) where T1 : T { bool hasValueOfGivenType = false; outValue = default; if (hasValue && value is T1 t1) { hasValueOfGivenType = true; outValue = t1; } return hasValueOfGivenType; } public bool TryUnwrap([NotNullWhen(returnValue: true)] out T? outValue) => TryUnwrap(out outValue); public Option Select(Func selector) where TType : notnull => TryUnwrap(out T? selfValue) ? Option.Some(selector(selfValue)) : Option.None; public Option Bind(Func> binder) where TType : notnull => TryUnwrap(out T? selfValue) ? binder(selfValue) : Option.None; public T Fallback(T fallback) => TryUnwrap(out var v) ? v : fallback; public Option Fallback(Option fallback) => IsSome() ? this : fallback; public static Option Some(T value) => typeof(T) switch { var t when t == typeof(bool) => throw new Exception("Option type rejects booleans"), {IsConstructedGenericType: true} t when t.GetGenericTypeDefinition() == typeof(Option<>) => throw new Exception("Option type rejects nested Option"), {IsConstructedGenericType: true} t when t.GetGenericTypeDefinition() == typeof(Nullable<>) => throw new Exception("Option type rejects Nullable"), _ => new Option(hasValue: true, value: value ?? throw new Exception("Option type rejects null")) }; public override bool Equals(object? obj) => obj switch { Option otherOption when otherOption.IsNone() => IsNone(), Option otherOption when otherOption.TryUnwrap(out var otherValue) => ValueEquals(otherValue), T otherValue => ValueEquals(otherValue), _ => false }; public bool ValueEquals(T otherValue) => TryUnwrap(out T? selfValue) && selfValue.Equals(otherValue); public override int GetHashCode() => TryUnwrap(out T? selfValue) ? selfValue.GetHashCode() : 0; public static bool operator ==(Option a, Option b) => a.Equals(b); public static bool operator !=(Option a, Option b) => !(a == b); public static Option None() => default; public static implicit operator Option(in Option.UnspecifiedNone _) => None(); public override string ToString() => TryUnwrap(out var selfValue) ? $"Some<{typeof(T).Name}>({selfValue})" : $"None<{typeof(T).Name}>"; } public static class Option { public static Option Some(T value) where T : notnull => Option.Some(value); public static UnspecifiedNone None => default; public readonly ref struct UnspecifiedNone { } } }