2023-08-01 22:12:54 -04:00
|
|
|
namespace FruityFoundation.Base.Structures;
|
2022-12-23 12:38:51 -05:00
|
|
|
|
|
|
|
using System;
|
|
|
|
|
2023-08-01 22:12:54 -04:00
|
|
|
public static class Maybe
|
|
|
|
{
|
2024-04-30 13:36:31 -04:00
|
|
|
public static Maybe<T> Create<T>(T value) => new(value);
|
|
|
|
|
|
|
|
public static Maybe<T> Create<T>(T value, Func<bool> evalIsEmpty) =>
|
|
|
|
evalIsEmpty()
|
|
|
|
? Empty<T>()
|
|
|
|
: new Maybe<T>(value);
|
|
|
|
|
|
|
|
public static Maybe<T> Empty<T>() => new();
|
2023-08-01 22:12:54 -04:00
|
|
|
}
|
2021-11-19 00:12:02 -05:00
|
|
|
|
2024-04-30 13:17:02 -04:00
|
|
|
public readonly record struct Maybe<T>
|
2021-11-19 00:12:02 -05:00
|
|
|
{
|
|
|
|
private readonly T _value;
|
2024-04-30 13:36:31 -04:00
|
|
|
public bool HasValue { get; }
|
2021-11-19 00:12:02 -05:00
|
|
|
|
2024-04-30 13:36:31 -04:00
|
|
|
internal Maybe(T val) : this()
|
2021-11-19 00:12:02 -05:00
|
|
|
{
|
|
|
|
_value = val;
|
2024-04-30 13:36:31 -04:00
|
|
|
HasValue = true;
|
2021-11-19 00:12:02 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
public T Value
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
if (!HasValue)
|
|
|
|
throw new InvalidOperationException($"{nameof(Maybe<T>)} value is empty");
|
|
|
|
|
|
|
|
return _value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-01 22:12:54 -04:00
|
|
|
public T OrValue(T orVal) => HasValue ? Value : orVal;
|
|
|
|
|
|
|
|
public T OrEval(Func<T> valueFactory) => HasValue ? Value : valueFactory();
|
2021-11-19 00:12:02 -05:00
|
|
|
|
|
|
|
public bool Try(out T val)
|
|
|
|
{
|
2023-08-01 22:12:54 -04:00
|
|
|
val = HasValue ? Value : default!;
|
2021-11-19 00:12:02 -05:00
|
|
|
|
|
|
|
return HasValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
public T OrThrow(string msg) =>
|
|
|
|
HasValue ? Value : throw new Exception(msg);
|
|
|
|
|
2023-08-01 22:12:54 -04:00
|
|
|
public T OrThrow(Func<string> messageFactory) =>
|
|
|
|
HasValue ? Value : throw new Exception(messageFactory());
|
|
|
|
|
|
|
|
public T OrThrow(Func<Exception> exFactory) =>
|
|
|
|
HasValue ? Value : throw exFactory();
|
2021-11-19 00:12:02 -05:00
|
|
|
|
2023-08-01 22:12:54 -04:00
|
|
|
public Maybe<TOutput> Map<TOutput>(Func<T, TOutput> transformer) =>
|
2024-04-30 13:18:51 -04:00
|
|
|
HasValue ? Maybe.Create(transformer(Value)) : Maybe.Empty<TOutput>();
|
2021-11-19 00:12:02 -05:00
|
|
|
|
2023-12-17 13:47:16 -05:00
|
|
|
public Maybe<TOutput> Bind<TOutput>(Func<T, Maybe<TOutput>> binder) =>
|
2023-08-01 22:12:54 -04:00
|
|
|
HasValue ? binder(Value) : Maybe.Empty<TOutput>();
|
2021-11-19 00:12:02 -05:00
|
|
|
|
2023-12-25 14:37:58 -05:00
|
|
|
public Maybe<T> EmptyBind(Func<Maybe<T>> maybeFactory) =>
|
|
|
|
HasValue ? this : maybeFactory();
|
|
|
|
|
|
|
|
public Maybe<T> EmptyBind(Maybe<T> maybe) =>
|
|
|
|
HasValue ? this : maybe;
|
|
|
|
|
2023-08-01 22:12:54 -04:00
|
|
|
public Maybe<TOutput> Cast<TOutput>()
|
|
|
|
{
|
|
|
|
if (!HasValue)
|
|
|
|
return Maybe.Empty<TOutput>();
|
2021-11-19 00:12:02 -05:00
|
|
|
|
2023-08-01 22:12:54 -04:00
|
|
|
try
|
|
|
|
{
|
2024-02-07 20:57:33 -05:00
|
|
|
var t = typeof(TOutput);
|
|
|
|
t = Nullable.GetUnderlyingType(t) ?? t;
|
|
|
|
|
|
|
|
var output = Value == null
|
|
|
|
? default
|
|
|
|
: (TOutput)Convert.ChangeType(Value, t);
|
|
|
|
|
2024-04-30 13:36:31 -04:00
|
|
|
if (output == null)
|
|
|
|
return Maybe.Empty<TOutput>();
|
|
|
|
|
|
|
|
return Maybe.Create(output);
|
2023-08-01 22:12:54 -04:00
|
|
|
}
|
|
|
|
catch (InvalidCastException)
|
|
|
|
{
|
|
|
|
return Maybe.Empty<TOutput>();
|
|
|
|
}
|
|
|
|
}
|
2021-11-19 00:12:02 -05:00
|
|
|
|
2024-04-30 13:18:51 -04:00
|
|
|
public static implicit operator Maybe<T>(T val) => Maybe.Create(val);
|
2021-11-19 00:12:02 -05:00
|
|
|
|
|
|
|
public static explicit operator T(Maybe<T> val) => val.Value;
|
|
|
|
|
|
|
|
public override string ToString()
|
|
|
|
{
|
|
|
|
return (Try(out var val)
|
|
|
|
? val?.ToString()
|
|
|
|
: base.ToString())!;
|
|
|
|
}
|
|
|
|
}
|