namespace FruityFoundation.Base.Structures; using System; public static class Maybe { public static Maybe Just(T value) => new(value, hasValue: true); public static Maybe Just(T value, Func hasValue) => new(value, hasValue: hasValue(value)); public static Maybe Empty() => new(val: default!, hasValue: false); } public readonly record struct Maybe { private readonly T _value; public readonly bool HasValue; internal Maybe(T val, bool hasValue) { _value = val; HasValue = hasValue; } 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.Just(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); return Maybe.Just(output, hasValue: _ => output != null)!; } catch (InvalidCastException) { return Maybe.Empty(); } } public static implicit operator Maybe(T val) => Maybe.Just(val); public static explicit operator T(Maybe val) => val.Value; public override string ToString() { return (Try(out var val) ? val?.ToString() : base.ToString())!; } }