Created
March 30, 2021 15:59
-
-
Save copygirl/b5bacf03a9814db6ac830f2ede602257 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Runtime.CompilerServices; | |
using System.Runtime.InteropServices; | |
namespace gaemstone.Utility | |
{ | |
public delegate ref TResult ValueSelector<TValue, TResult>(ref TValue value); | |
public delegate bool ValueFilter<TValue>(ref TValue value); | |
public delegate void ValueAction<TValue>(ref TValue value); | |
public readonly struct Option<TValue> | |
{ | |
readonly TValue _value; | |
public bool IsSome { get; } | |
public bool IsNone => !IsSome; | |
public TValue ValueUnchecked => _value; | |
public TValue Value => OrThrow(); | |
Option(bool isSome, TValue value) { IsSome = isSome; _value = value; } | |
public static Option<TValue> Some(TValue value) => new(true, value); | |
public static Option<TValue> None { get; } = new(false, default!); | |
public Option<TResult> Select<TResult>(Func<TValue, TResult> selectFunc) | |
=> IsSome ? Option<TResult>.Some(selectFunc(ValueUnchecked)) | |
: Option<TResult>.None; | |
public Option<TValue> Where(Func<TValue, bool> whereFunc) | |
=> (IsSome && whereFunc(ValueUnchecked)) ? this : Option<TValue>.None; | |
public Option<TValue> Then(Action<TValue> thenFunc) | |
{ if (IsSome) thenFunc(ValueUnchecked); return this; } | |
public TValue OrDefault() | |
=> IsSome ? ValueUnchecked : default!; | |
public TValue Or(TValue elseValue) | |
=> IsSome ? ValueUnchecked : elseValue; | |
public TValue Or(Func<TValue> elseFunc) | |
=> IsSome ? ValueUnchecked : elseFunc(); | |
public TValue OrThrow() => OrThrow(() | |
=> throw new InvalidOperationException("Option is none")); | |
public TValue OrThrow(Func<Exception> errorFunc) | |
=> IsSome ? ValueUnchecked : throw errorFunc(); | |
public Result<TValue, TError> ToResult<TError>(Func<Option<TValue>, Result<TValue, TError>> converter) | |
=> converter(this); | |
public IEnumerable<TValue> ToEnumerable() | |
{ if (IsSome) yield return ValueUnchecked; } | |
} | |
public readonly ref struct OptionRef<TValue> | |
{ | |
public delegate ResultRef<TValue, TError> ResultConverter<TError>(OptionRef<TValue> option); | |
readonly IntPtr _value; | |
public bool IsSome => _value != IntPtr.Zero; | |
public bool IsNone => _value == IntPtr.Zero; | |
public ref TValue ValueUnchecked | |
{ get { unsafe { return ref Unsafe.AsRef<TValue>((void*)_value); } } } | |
public ref TValue Value => ref OrThrow(); | |
// Internal so ResultRef can easily be converted. | |
internal OptionRef(IntPtr value) { _value = value; } | |
public static OptionRef<TValue> Some(ref TValue value) | |
{ unsafe { return new((IntPtr)Unsafe.AsPointer(ref value)); } } | |
public static OptionRef<TValue> None() => new(IntPtr.Zero); | |
public OptionRef<TResult> Select<TResult>(ValueSelector<TValue, TResult> selectFunc) | |
=> IsSome ? OptionRef<TResult>.Some(ref selectFunc(ref ValueUnchecked)) | |
: OptionRef<TResult>.None(); | |
public Option<TResult> Select<TResult>(Func<TValue, TResult> selectFunc) | |
=> IsSome ? Option<TResult>.Some(selectFunc(ValueUnchecked)) | |
: Option<TResult>.None; | |
public OptionRef<TValue> Where(ValueFilter<TValue> whereFunc) | |
=> (IsSome && whereFunc(ref ValueUnchecked)) ? this : OptionRef<TValue>.None(); | |
public OptionRef<TValue> Then(ValueAction<TValue> thenFunc) | |
{ if (IsSome) thenFunc(ref ValueUnchecked); return this; } | |
public OptionRef<TValue> Then(Action<TValue> thenFunc) | |
{ if (IsSome) thenFunc(ValueUnchecked); return this; } | |
public ref TValue OrNull() | |
=> ref ValueUnchecked; | |
public TValue OrDefault() | |
=> IsSome ? ValueUnchecked : default!; | |
public TValue Or(TValue elseValue) | |
=> IsSome ? ValueUnchecked : elseValue; | |
public TValue Or(Func<TValue> elseFunc) | |
=> IsSome ? ValueUnchecked : elseFunc(); | |
public ref TValue OrThrow() => ref OrThrow(() | |
=> throw new InvalidOperationException("Option is none")); | |
public ref TValue OrThrow(Func<Exception> errorFunc) | |
{ if (IsSome) return ref ValueUnchecked; else throw errorFunc(); } | |
public ResultRef<TValue, TError> ToResult<TError>(ResultConverter<TError> converter) | |
=> converter(this); | |
public IEnumerable<TValue> ToEnumerable() | |
{ if (IsSome) yield return ValueUnchecked; } | |
} | |
public static class OptionExtensions | |
{ | |
public static T? OrNullable<T>(this Option<T> option) | |
where T : struct => option.IsSome ? option.ValueUnchecked : null; | |
public static T? OrNullable<T>(this OptionRef<T> option) | |
where T : struct => option.IsSome ? option.ValueUnchecked : null; | |
} | |
[StructLayout(LayoutKind.Explicit)] | |
public readonly struct Result<TValue, TError> | |
{ | |
[FieldOffset(4)] | |
readonly TValue _value; | |
[FieldOffset(4)] | |
readonly TError _error; | |
[field:FieldOffset(0)] | |
public bool IsSuccess { get; } | |
public bool IsError => !IsSuccess; | |
public TValue ValueUnchecked => _value; | |
public TError ErrorUnchecked => _error; | |
public TValue Value => OrThrow(); | |
#pragma warning disable CS8618 | |
Result(TValue value) : this() { IsSuccess = true; _value = value; } | |
Result(TError error) : this() { IsSuccess = false; _error = error; } | |
#pragma warning restore | |
public static Result<TValue, TError> Success(TValue value) => new(value); | |
public static Result<TValue, TError> Error(TError error) => new(error); | |
public Result<TResult, TError> Select<TResult>(Func<TValue, TResult> selectFunc) | |
=> IsSuccess ? Result<TResult, TError>.Success(selectFunc(ValueUnchecked)) | |
: Result<TResult, TError>.Error(ErrorUnchecked); | |
public Result<TValue, TError> Then(Action<TValue> thenFunc) | |
{ if (IsSuccess) thenFunc(ValueUnchecked); return this; } | |
public TValue OrDefault() | |
=> IsSuccess ? ValueUnchecked : default!; | |
public TValue Or(TValue elseValue) | |
=> IsSuccess ? ValueUnchecked : elseValue; | |
public TValue Or(Func<TValue> elseFunc) | |
=> IsSuccess ? ValueUnchecked : elseFunc(); | |
public TValue Or(Func<TError, TValue> elseFunc) | |
=> IsSuccess ? ValueUnchecked : elseFunc(ErrorUnchecked); | |
public TValue OrThrow() => OrThrow(err => throw err switch { | |
Exception ex => ex, | |
_ => new InvalidOperationException($"Result is error: {err?.ToString() ?? "null"}"), | |
}); | |
public TValue OrThrow(Func<Exception> errorFunc) | |
=> OrThrow(_ => errorFunc()); | |
public TValue OrThrow(Func<TError, Exception> errorFunc) | |
=> IsSuccess ? ValueUnchecked : throw errorFunc(ErrorUnchecked); | |
public Option<TValue> ToOption() | |
=> IsSuccess ? Option<TValue>.Some(ValueUnchecked) : Option<TValue>.None; | |
public IEnumerable<TValue> ToEnumerable() | |
=> IsSuccess ? new []{ ValueUnchecked } : Enumerable.Empty<TValue>(); | |
} | |
public readonly ref struct ResultRef<TValue, TError> | |
{ | |
readonly IntPtr _value; | |
readonly TError _error; | |
public bool IsSuccess => _value != IntPtr.Zero; | |
public bool IsError => _value == IntPtr.Zero; | |
public ref TValue ValueUnchecked | |
{ get { unsafe { return ref Unsafe.AsRef<TValue>((void*)_value); } } } | |
public TError ErrorUnchecked => _error; | |
public ref TValue Value => ref OrThrow(); | |
ResultRef(IntPtr value, TError error) | |
{ _value = value; _error = error; } | |
public static ResultRef<TValue, TError> Success(ref TValue value) | |
{ unsafe { return new((IntPtr)Unsafe.AsPointer(ref value), default!); } } | |
public static ResultRef<TValue, TError> Error(TError value) | |
=> new(IntPtr.Zero, value); | |
public ResultRef<TResult, TError> Select<TResult>(ValueSelector<TValue, TResult> selectFunc) | |
=> IsSuccess ? ResultRef<TResult, TError>.Success(ref selectFunc(ref ValueUnchecked)) | |
: ResultRef<TResult, TError>.Error(ErrorUnchecked); | |
public Result<TResult, TError> Select<TResult>(Func<TValue, TResult> selectFunc) | |
=> IsSuccess ? Result<TResult, TError>.Success(selectFunc(ValueUnchecked)) | |
: Result<TResult, TError>.Error(ErrorUnchecked); | |
public ResultRef<TValue, TError> Then(ValueAction<TValue> thenFunc) | |
{ if (IsSuccess) thenFunc(ref ValueUnchecked); return this; } | |
public ResultRef<TValue, TError> Then(Action<TValue> thenFunc) | |
{ if (IsSuccess) thenFunc(ValueUnchecked); return this; } | |
public ref TValue OrNull() | |
=> ref IsSuccess ? ref ValueUnchecked : ref Unsafe.NullRef<TValue>(); | |
public TValue OrDefault() | |
=> IsSuccess ? ValueUnchecked : default!; | |
public TValue Or(TValue elseValue) | |
=> IsSuccess ? ValueUnchecked : elseValue; | |
public TValue Or(Func<TValue> elseFunc) | |
=> IsSuccess ? ValueUnchecked : elseFunc(); | |
public TValue Or(Func<TError, TValue> elseFunc) | |
=> IsSuccess ? ValueUnchecked : elseFunc(ErrorUnchecked); | |
public ref TValue OrThrow() => ref OrThrow(err => throw err switch { Exception ex => ex, | |
_ => new InvalidOperationException($"Result is error: {err?.ToString() ?? "null"}") }); | |
public ref TValue OrThrow(Func<Exception> errorFunc) | |
=> ref OrThrow(_ => errorFunc()); | |
public ref TValue OrThrow(Func<TError, Exception> errorFunc) | |
{ if (IsSuccess) return ref ValueUnchecked; else throw errorFunc(ErrorUnchecked); } | |
public OptionRef<TValue> ToOption() => new(_value); | |
public IEnumerable<TValue> ToEnumerable() | |
{ if (IsSuccess) yield return ValueUnchecked; } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment