Skip to content

Instantly share code, notes, and snippets.

@copygirl
Created March 30, 2021 15:59
Show Gist options
  • Save copygirl/b5bacf03a9814db6ac830f2ede602257 to your computer and use it in GitHub Desktop.
Save copygirl/b5bacf03a9814db6ac830f2ede602257 to your computer and use it in GitHub Desktop.
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