namespace FruityFoundation.Base.Structures; using System; public static class Maybe { public static Maybe Create(T value) => new(value); public static Maybe Create(T value, Func evalIsEmpty) => evalIsEmpty() ? Empty() : new Maybe(value); public static Maybe Empty() => new(); } public readonly record struct Maybe { private readonly T _value; public bool HasValue { get; } internal Maybe(T val) : this() { _value = val; HasValue = true; } public T Value { get { if (!HasValue) throw new InvalidOperationException($"{nameof(Maybe)} value is empty"); return _value; } } public T OrValue(T orVal) => HasValue ? Value : orVal; public T OrEval(Func valueFactory) => HasValue ? Value : valueFactory(); public bool Try(out T val) { val = HasValue ? Value : default!; return HasValue; } public T OrThrow(string msg) => HasValue ? Value : throw new Exception(msg); public T OrThrow(Func messageFactory) => HasValue ? Value : throw new Exception(messageFactory()); public T OrThrow(Func exFactory) => HasValue ? Value : throw exFactory(); public Maybe Map(Func transformer) => HasValue ? Maybe.Create(transformer(Value)) : Maybe.Empty(); public Maybe Bind(Func> binder) => HasValue ? binder(Value) : Maybe.Empty(); public Maybe EmptyBind(Func> maybeFactory) => HasValue ? this : maybeFactory(); public Maybe EmptyBind(Maybe maybe) => HasValue ? this : maybe; public Maybe Cast() { if (!HasValue) return Maybe.Empty(); try { var t = typeof(TOutput); t = Nullable.GetUnderlyingType(t) ?? t; var output = Value == null ? default : (TOutput)Convert.ChangeType(Value, t); if (output == null) return Maybe.Empty(); return Maybe.Create(output); } catch (InvalidCastException) { return Maybe.Empty(); } } public static implicit operator Maybe(T val) => Maybe.Create(val); public static explicit operator T(Maybe val) => val.Value; public override string ToString() { return (Try(out var val) ? val?.ToString() : base.ToString())!; } }