FruityFoundation/Base/Structures/Maybe.cs

149 lines
2.8 KiB
C#
Raw Permalink Normal View History

2023-08-01 22:12:54 -04:00
namespace FruityFoundation.Base.Structures;
using System;
2024-05-03 00:27:31 -04:00
public delegate bool TryParseDelegate<in TInput, TOutput>(TInput input, out TOutput output);
2023-08-01 22:12:54 -04:00
public static class Maybe
{
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);
2024-05-03 00:27:31 -04:00
public static Maybe<TOutput> TryParse<TInput, TOutput>(TInput value, TryParseDelegate<TInput, TOutput> tryParse)
{
if (!tryParse(value, out var output))
return Empty<TOutput>();
return new Maybe<TOutput>(output);
}
public static Maybe<T> Empty<T>() => new();
2023-08-01 22:12:54 -04:00
}
2021-11-19 00:12:02 -05:00
public readonly record struct Maybe<T>
2021-11-19 00:12:02 -05:00
{
private readonly T _value;
public bool HasValue { get; }
2021-11-19 00:12:02 -05:00
internal Maybe(T val) : this()
2021-11-19 00:12:02 -05:00
{
_value = val;
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;
}
}
2024-04-30 14:17:54 -04:00
public T OrValue(T orVal)
{
if (!HasValue)
return orVal;
return Value;
}
public T OrEval(Func<T> valueFactory)
{
if (!HasValue)
return valueFactory();
2023-08-01 22:12:54 -04:00
2024-04-30 14:17:54 -04:00
return Value;
}
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;
}
2024-04-30 14:17:54 -04:00
public T OrThrow(string msg)
{
if (!HasValue)
throw new ApplicationException(msg);
return Value;
}
public T OrThrow(Func<string> messageFactory)
{
if (!HasValue)
throw new ApplicationException(messageFactory());
return Value;
}
public T OrThrow(Func<Exception> exFactory)
{
if (!HasValue)
throw exFactory();
2021-11-19 00:12:02 -05:00
2024-04-30 14:17:54 -04:00
return Value;
}
2023-08-01 22:12:54 -04:00
2024-04-30 14:17:54 -04:00
public Maybe<TOutput> Map<TOutput>(Func<T, TOutput> transformer)
{
if (!HasValue)
return Maybe.Empty<TOutput>();
2021-11-19 00:12:02 -05:00
2024-04-30 14:17:54 -04:00
var newValue = transformer(Value);
return new Maybe<TOutput>(newValue);
}
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
{
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<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
public static implicit operator Maybe<T>(T val) => Maybe.Create(val);
2021-11-19 00:12:02 -05:00
public override string ToString()
{
2024-04-30 14:17:54 -04:00
if (!HasValue)
return "<empty>";
return _value?.ToString() ?? "<null>";
2021-11-19 00:12:02 -05:00
}
}