Skip to content

Instantly share code, notes, and snippets.

@kallebysantos
Last active January 25, 2024 09:39
Show Gist options
  • Save kallebysantos/0653a8298eaea9b40c3f43f3db3f9430 to your computer and use it in GitHub Desktop.
Save kallebysantos/0653a8298eaea9b40c3f43f3db3f9430 to your computer and use it in GitHub Desktop.
Rust types implementation in CSharp
public abstract record Option<T>()
{
    internal T? Value { get; init; }

    protected Option(T Value) : this()
    {
        this.Value = Value;
    }

    public static implicit operator Option<T>(T? value) =>
        (value is not null) ? new Some<T>(value) : new None<T>();
}

public record Some<T>(T Value) : Option<T>(Value)
{
    public new T Value { get; } = Value;

    public static implicit operator T(Some<T> some) => some.Value!;

    public static implicit operator Some<T>(T value) => new(value);
}

public sealed record None<T>() : Option<T>;

public static class Option
{
    public static Option<T> From<T>(T? Value) => Value is null ? None<T>() : Some(Value);

    public static Some<T> Some<T>(T Value) => new(Value);

    public static None<T> None<T>() => new();

    public static Option<T> Default<T>() => default(T);

    public static bool IsSome<T>(this Option<T> option) => option is Some<T>;

    public static bool IsNone<T>(this Option<T> option) => option is None<T>;

    public static Option<U> Map<T, U>(this Option<T> option, Func<T, U> some) =>
        option is { Value: T value }
        ? some(value)
        : None<U>();

    public static U? MapOrDefault<T, U>(this Option<T> option, Func<T, U> some) =>
        option is { Value: T value }
        ? some(value)
        : default;

    public static Option<T> Filter<T>(this Option<T> option, Func<T, bool> predicate) =>
        option is { Value: T value } && predicate(value)
        ? Some(value) 
        : None<T>();
        

    public static void Then<T>(this Option<T> option, Action<T> some)
    {
        if (option is { Value: T value })
            some(value);
    }

    public static void Match<T>(this Option<T> option, Action<T> some, Action none)
    {
        if (option is { Value: T value })
            some(value);
        else
            none();        
    }
    
    public static U Match<T, U>(this Option<T> option, Func<T, U> some, Func<U> none) =>
        option is { Value: T value }
        ? some(value) 
        : none();

    public static T Expect<T>(this Option<T> option) =>
        Expect(option, $"Expect: {nameof(option)} to be {nameof(Some)} but found {nameof(None)}");

    public static T Expect<T>(this Option<T> option, string message) =>
        Expect(option, new Exception(message));
    
    public static T Expect<T>(this Option<T> option, Exception ex) =>
        option.Value ?? throw ex;

    public static T UnwrapOr<T>(this Option<T> option, T @default) => option.Value ?? @default;
}
public record Result<T, E>
{
    internal Option<T> OkValue { get; init; } = Option.None<T>();
    
    internal Option<E> ErrValue { get; init; } = Option.None<E>();

    internal Result() {}

    protected Result(T Value) : this()
    {
        OkValue = Option.Some(Value);
        ErrValue = Option.None<E>();
    }
    
    protected Result(E Error) : this()
    {
        ErrValue = Option.Some(Error);
        OkValue = Option.None<T>();
    }

    internal Result(Option<T> OkValue, Option<E> ErrValue) : this()
    {
        this.OkValue = OkValue;
        this.ErrValue = ErrValue;
    }

    public static implicit operator Result<T, E>(T value) => new Ok<T, E>(value);

    public static implicit operator Result<T, E>(E Error) => new Err<T, E>(Error);
}

public sealed record Ok<T, E>(T Value) : Result<T, E>(Value)
{
    public static implicit operator T(Ok<T, E> ok) => ok.Value;

    public static implicit operator Ok<T, E>(T value) => new(value);
}

public record Err<T,E>(E Value) : Result<T, E>(Value)
{   
    public static implicit operator E(Err<T, E> err) => err.Value;

    public static implicit operator Err<T, E>(E err) => new(err);
}

public static class Result
{
    public static Option<T> Ok<T, E>(this Result<T, E> result) => result.OkValue;

    public static Option<E> Err<T, E>(this Result<T, E> result) => result.ErrValue;

    public static Ok<T, E> Ok<T, E>(T Value) => new(Value);

    public static Err<T, E> Err<T, E>(E Value) => new(Value);

    public static bool IsOk<T, E>(this Result<T, E> result) => result is Ok<T, E>;

    public static bool IsErr<T, E>(this Result<T, E> result) => result is Err<T, E>;

    public static Result<U, E> Map<T, E, U>(this Result<T, E> result, Func<T, U> ok)
        => new(result.OkValue.Map(ok), result.ErrValue);
    
    public static Result<T, F> MapErr<T, E, F>(this Result<T, E> result, Func<E, F> err)
        => new(result.OkValue, result.ErrValue.Map(err));

    public static void Match<T, E>(this Result<T, E> result, Action<T> ok, Action<E> err)
    {
        if (result is { OkValue: Some<T> okValue })
            ok(okValue);
        
        if (result is { ErrValue: Some<E> errValue })
            err(errValue);
    }

    public static U Match<T, E, U>(this Result<T, E> result, Func<T, U> ok, Func<E, U> err) =>
        result switch
        {
            { OkValue: T value } => ok(value),
            { ErrValue: E value } => err(value),
            _ => default!,
        };

    public static T Expect<T, E>(this Result<T, E> result)
        => Expect(result, $"Expect: {nameof(result)} to be {nameof(Ok)} but found {nameof(Err)}");
    
    public static T Expect<T, E>(this Result<T, E> result, string message)
        => Expect(result, new Exception(message));
    
    public static T Expect<T, E>(this Result<T, E> result, Exception ex) 
        => result.OkValue.Expect(ex);

    public static E ExpectErr<T, E>(this Result<T, E> result)
        => ExpectErr(result, $"Expect: {nameof(result)} to be {nameof(Err)} but found {nameof(Ok)}");
    
    public static E ExpectErr<T, E>(this Result<T, E> result, string message)
        => ExpectErr(result, new Exception(message));

    public static E ExpectErr<T, E>(this Result<T, E> result, Exception ex) 
        => result.ErrValue.Expect(ex);

    public static T UnwrapOr<T, E>(this Result<T, E> result, T @default) 
        => result.OkValue.UnwrapOr(@default);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment