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) { if (!HasValue) return orVal; return Value; } public T OrEval(Func valueFactory) { if (!HasValue) return valueFactory(); return Value; } public bool Try(out T val) { val = HasValue ? Value : default!; return HasValue; } public T OrThrow(string msg) { if (!HasValue) throw new ApplicationException(msg); return Value; } public T OrThrow(Func messageFactory) { if (!HasValue) throw new ApplicationException(messageFactory()); return Value; } public T OrThrow(Func exFactory) { if (!HasValue) throw exFactory(); return Value; } public Maybe Map(Func transformer) { if (!HasValue) return Maybe.Empty(); var newValue = transformer(Value); return new Maybe(newValue); } 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 override string ToString() { if (!HasValue) return ""; return _value?.ToString() ?? ""; } }