using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using JetBrains.Lifetimes;
using System.Threading.Tasks;
using JetBrains.Diagnostics;
using JetBrains.Threading;
using System.Runtime.ExceptionServices;
#nullable disable
namespace JetBrains.Core
{
///
/// Helper methods for and building
///
[PublicAPI]
public static class Result
{
///
/// Message that is being applied to Result.Fail when no message provided
///
public const string EmptyFailMessage = "<>";
///
/// Creates successful with value
///
///
///
/// Result with == true
public static Result Success(T value)
{
return new Result(value, null);
}
///
/// Creates failed
///
///
/// Try to capture exception stack (if any), could be unwind by
/// Result with == false. Returned type could be implicitly casted to any
/// if is null
public static Result Fail([NotNull] Exception exception, bool captureStackTrace = false)
{
if (exception == null) throw new ArgumentNullException(nameof(exception));
return new Result(null, captureStackTrace ? ExceptionDispatchInfo.Capture(exception) : (object) exception);
}
///
/// Creates failed with corresponding
///
///
/// Special user-defined value provided for failed Result
/// Try to capture exception stack (if any), could be unwind by
/// Result with == false. Returned type could be implicitly casted to any
/// if is null
public static Result Fail([NotNull] Exception exception, TFailure failValue, bool captureStackTrace = false)
{
if (exception == null) throw new ArgumentNullException(nameof(exception));
return new Result(null, captureStackTrace ? ExceptionDispatchInfo.Capture(exception) : (object) exception, failValue);
}
///
/// Creates failed with that wraps provided
///
/// Reason of failure. If not defined, is used.
/// Result with == false. Returned type could be implicitly casted to any
public static Result Fail(string message = null)
{
return Fail(new ResultException(message ?? EmptyFailMessage));
}
///
/// Creates failed with that wraps provided
///
/// Reason of failure. If not defined, is used.
/// Special user-defined value provided for failed Result
/// Result with == false. Returned type could be implicitly casted to any
public static Result Fail(string message, TFailure failValue)
{
return Fail(new ResultException(message ?? EmptyFailMessage), failValue);
}
///
/// Creates failed with message= and user-defined failure parameter
///
/// Special user-defined value provided for failed Result
/// Result with == false. Returned type could be implicitly casted to any
public static Result FailWithValue(TFailure failValue)
{
return Fail((string) null, failValue);
}
///
/// Creates special failed that wraps
///
/// Result with == false and == true. Returned type could be implicitly casted to any
public static Result Canceled()
{
return Canceled(new OperationCanceledException(
Lifetime.Terminated
));
}
///
/// Creates special failed that wraps
///
/// Captured OCE that lead to this cancellation
/// Try to capture exception stack (if any), could be unwind by .
/// Result with == false and == true. Returned type could be implicitly casted to any
/// if is null
public static Result Canceled([NotNull] OperationCanceledException exception, bool captureStackTrace = false)
{
return Fail(exception, captureStackTrace);
}
///
/// Void succeed result for type
///
public static Result Unit = Success(Core.Unit.Instance);
///
/// Wrap execution of () into .
///
/// Function to execute
/// type argument of returned Result
/// Succeed result with == f() if no exception happened during execution. Failed result with corresponding exception otherwise
#if !NETCOREAPP
[System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions]
#endif
public static Result Wrap([NotNull] Func f)
{
try
{
return Success(f());
}
catch (Exception e)
{
return Fail(e, true);
}
}
///
/// Wrap execution of () into .
///
/// Action to execute
/// Succeed result with if no exception happened during execution. Failed result with corresponding exception otherwise
#if !NETCOREAPP
[System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions]
#endif
public static Result Wrap([NotNull] Action f)
{
try
{
f();
return Unit;
}
catch (Exception e)
{
return Fail(e, true);
}
}
///
/// Wrap execution of () into .
///
/// Action with parameter to execute
/// function argument
/// type
/// Succeed result with if no exception happened during execution. Failed result with corresponding exception otherwise
#if !NETCOREAPP
[System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions]
#endif
public static Result Wrap([NotNull] Action f, T param)
{
try
{
f(param);
return Unit;
}
catch (Exception e)
{
return Fail(e, true);
}
}
///
/// Wrap execution of () into .
///
/// Function with parameter to execute
/// function argument
/// type
/// type argument of returned Result
/// Succeed result with == f(param) if no exception happened during execution. Failed result with corresponding exception otherwise
#if !NETCOREAPP
[System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions]
#endif
public static Result Wrap([NotNull] Func f, T param)
{
try
{
return Success(f(param));
}
catch (Exception e)
{
return Fail(e, true);
}
}
///
/// Transforms this into .
/// corresponds to in .
/// corresponds to completed task with
/// corresponds to completed task with and
///
/// this
/// that corresponds
public static Task UnwrapTask(this Result result)
{
if (result.Succeed)
return result.Value;
if (!result.Canceled)
return Task.FromException(result.Exception);
if (result.Exception is OperationCanceledException oce)
return Task.FromCanceled(oce.CancellationToken);
else
return Task.FromCanceled(Lifetime.Terminated);
}
///
/// Transforms this into .
/// corresponds to in .
/// corresponds to completed task with
/// corresponds to completed task with and
///
/// this
/// type parameter of returning task
/// that corresponds
public static Task UnwrapTask(this Result> result)
{
if (result.Succeed)
return result.Value;
if (!result.Canceled)
return Task.FromException(result.Exception);
if (result.Exception is OperationCanceledException oce)
return Task.FromCanceled(oce.CancellationToken);
else
return Task.FromCanceled(Lifetime.Terminated);
}
///
/// Wrap completed task's result into or throw is task is !
///
/// Must be finished (==true) or will be throws
///
/// (task.) or (task.)
/// in case of !task.
public static Result FromCompletedTask(Task task)
{
if (!task.IsCompleted)
throw new InvalidOperationException($"Task must be completed to convert into result but was in state: {task.Status}");
return task.Status == TaskStatus.RanToCompletion ?
Success(task.Result)
: Fail(task.Exception.NotNull($"Exception must always exist for task with status: {task.Status}"));
}
}
///
/// Monad that can can have two states: and Fail (!). Also we distinct special type of Fail: .
///
///
[PublicAPI]
public readonly struct Result : IEquatable>
{
///
/// Value in case of , default(T) otherwise
///
public readonly T Value;
///
/// It this field not null, this Result is ! and vise versa.
///
internal readonly object ExceptionOrExceptionDispatchInfo;
///
/// Exception in case of (!), null otherwise
///
public Exception Exception => ExceptionOrExceptionDispatchInfo is Exception ex
? ex
: (ExceptionOrExceptionDispatchInfo as ExceptionDispatchInfo)?.SourceException;
///
/// Exception message in case of (!), null otherwise
///
public string FailMessage => Exception?.Message;
///
/// Shouldn't be invoked in user's code
///
///
///
internal Result(T success, object failure)
{
Value = success;
ExceptionOrExceptionDispatchInfo = failure;
}
///
/// Is result successful
///
public bool Succeed => ExceptionOrExceptionDispatchInfo == null;
///
/// (!) and (
///
public bool FailedNotCanceled => !Succeed && !Canceled;
///
/// Exception has specials type of or that has inside.
///
public bool Canceled => Exception.IsOperationCanceled();
public static implicit operator Result(Result me)
{
return new Result(default(T), me.ExceptionOrExceptionDispatchInfo);
}
///
/// Transform this result into new one with given function. if !, stays untouched./>
///
///
///
///
public Result Map(Func transform)
{
return Succeed ? Result.Wrap(transform, Value) : Result.Fail(Exception);
}
///
/// Map without lambda. Success{Anything} -> Success{}. Fail -> Fail
///
/// In case of success we always create successful result with this value
///
///
public Result Map(TRes successValue)
{
return Succeed ? Result.Success(successValue) : Result.Fail(Exception);
}
///
/// Returns if , throws otherwise
///
/// if
/// if !
public T Unwrap()
{
if (Succeed)
return Value;
switch (ExceptionOrExceptionDispatchInfo)
{
case Exception ex:
throw ex;
case ExceptionDispatchInfo edi:
edi.Throw();
return Nothing.Unreachable();
default:
return Nothing.Unreachable();
}
}
///
/// Transforms this into in state state.
/// corresponds to .
/// corresponds to
/// corresponds to with
///
/// in state
public Task AsCompletedTask()
{
if (Succeed)
return Task.FromResult(Value);
if (!Canceled) return Task.FromException(Exception);
if (Exception is OperationCanceledException oce)
return Task.FromCanceled(oce.CancellationToken);
else
return Task.FromCanceled(Lifetime.Terminated);
}
public override string ToString()
{
var status = Succeed ? "Success(" + Value +")":
Canceled ? "Canceled"
: "Fail(" + FailMessage+")";
return "Result." + status;
}
public bool Equals(Result other)
{
return EqualityComparer.Default.Equals(Value, other.Value) && Equals(Exception, other.Exception);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
return obj is Result other && Equals(other);
}
public override int GetHashCode()
{
unchecked
{
return (EqualityComparer.Default.GetHashCode(Value) * 397) ^ (Exception != null ? Exception.GetHashCode() : 0);
}
}
public static bool operator ==(Result left, Result right)
{
return left.Equals(right);
}
public static bool operator !=(Result left, Result right)
{
return !left.Equals(right);
}
}
///
/// Special kind of to store custom in case of !.
///
///
///
[PublicAPI]
public readonly struct Result : IEquatable>
{
///
/// Value in case of , default(T) otherwise
///
public readonly TSuccess Value;
///
/// It this field not null, this Result is ! and vise versa.
///
internal readonly object ExceptionOrExceptionDispatchInfo;
///
/// Exception in case of (!), null otherwise
///
public Exception Exception => ExceptionOrExceptionDispatchInfo is Exception ex
? ex
: (ExceptionOrExceptionDispatchInfo as ExceptionDispatchInfo)?.SourceException;
public string FailMessage => Exception?.Message;
public readonly TFailure FailValue;
internal Result(TSuccess success, object failure, TFailure failValue)
{
Value = success;
ExceptionOrExceptionDispatchInfo = failure;
FailValue = failValue;
}
public bool Succeed => ExceptionOrExceptionDispatchInfo == null;
public bool FailedNotCanceled => !Succeed && !Canceled;
public bool Canceled =>
Exception is OperationCanceledException
||((Exception as AggregateException)?.Flatten().InnerExceptions.Any(ex => ex is OperationCanceledException) ?? false)
;
public static implicit operator Result(Result me)
{
return new Result(default(TSuccess), me.ExceptionOrExceptionDispatchInfo, me.FailValue);
}
public static implicit operator Result(Result me)
{
return new Result(me.Value, me.ExceptionOrExceptionDispatchInfo);
}
public static implicit operator Result(Result me)
{
return new Result(me.Value, me.ExceptionOrExceptionDispatchInfo, default(TFailure));
}
public TSuccess Unwrap()
{
if (Succeed)
return Value;
switch (ExceptionOrExceptionDispatchInfo)
{
case Exception ex:
throw ex;
case ExceptionDispatchInfo edi:
edi.Throw();
return Nothing.Unreachable();
default:
return Nothing.Unreachable();
}
}
public override string ToString()
{
var status = Succeed ? "Success(" + Value +")":
Canceled ? "Canceled"
: $"Fail({FailMessage}, {FailValue})";
return "Result." + status;
}
public bool Equals(Result other)
{
return EqualityComparer.Default.Equals(Value, other.Value) && Equals(Exception, other.Exception) && EqualityComparer.Default.Equals(FailValue, other.FailValue);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
return obj is Result other && Equals(other);
}
public override int GetHashCode()
{
unchecked
{
var hashCode = EqualityComparer.Default.GetHashCode(Value);
hashCode = (hashCode * 397) ^ (Exception != null ? Exception.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ EqualityComparer.Default.GetHashCode(FailValue);
return hashCode;
}
}
public static bool operator ==(Result left, Result right)
{
return left.Equals(right);
}
public static bool operator !=(Result left, Result right)
{
return !left.Equals(right);
}
}
///
/// Exception arising in when do not specify exception explicitly:
///
public class ResultException : Exception
{
public ResultException(string message) : base(message) { }
}
}