namespace FinancialApi.Domain; public interface IFinancialError { int ErrorCode { get; } string Message { get; } Exception? Exception { get; } } public sealed class FinancialError : IFinancialError { public FinancialError(int errorCode, string message, Exception? exception = null) { ArgumentOutOfRangeException.ThrowIfNegativeOrZero(errorCode); ErrorCode = errorCode; Message = message ?? throw new ArgumentNullException(nameof(message)); Exception = exception; } public int ErrorCode { get; } public string Message { get; } public Exception? Exception { get; } public override string ToString() => $"Error: {ErrorCode} - {Message}{(Exception == null ? "" : $"\n{Exception}")}"; } public sealed class FinancialResult { private readonly T? _resultValue; private readonly FinancialError? _resultError; public FinancialResult(T resultValue) { IsError = false; _resultValue = resultValue; _resultError = null; } public FinancialResult(FinancialError resultError) { IsError = true; _resultValue = default; _resultError = resultError; } public bool IsError { get; } /// /// Error indicator /// public bool IsSuccess => !IsError; /// /// Result Values /// public T? Value => _resultValue; public FinancialError? Error => _resultError; public static implicit operator FinancialResult(T resultValue) => new(resultValue); public static implicit operator FinancialResult(FinancialError resultError) => new(resultError); public static FinancialResult Success(T successValue) { return new FinancialResult(successValue); } public static FinancialResult Failure(FinancialError failureValue) { return new FinancialResult(failureValue); } public TResult Match( Func success, Func failure, Func nullValue ) { return IsError switch { false when _resultValue == null => nullValue(), true => failure(_resultError!), _ => success(_resultValue) }; } public TResult Match(Func success, Func failure) { return IsError switch { true => failure(_resultError!), _ => success(_resultValue!) }; } public TResult? Match(Func success) { return IsError switch { true => default, _ => success(_resultValue!) }; } public void Match(Action success, Action failure, Action nullValue) { switch (IsError) { case false when _resultValue == null: nullValue.Invoke(); return; case true: failure.Invoke(_resultError!); return; default: success(_resultValue); break; } } public void Match(Action success, Action failure) { switch (IsError) { case true: failure.Invoke(_resultError!); return; default: success(_resultValue!); break; } } public void Match(Action success) { if (IsError) { return; } success(_resultValue!); } public async Task MatchAsync( Func> success, Func> failure, Func> nullValue ) { return IsError switch { false when _resultValue == null => await nullValue(), true => await failure(_resultError!), _ => await success(_resultValue) }; } public async Task MatchAsync( Func> success, Func> failure ) { return IsError switch { true => await failure(_resultError!), _ => await success(_resultValue!) }; } public async Task MatchAsync(Func> success) { return IsError switch { true => default, _ => await success(_resultValue!) }; } public async Task MatchAsync(Func success, Func failure, Func nullValue) { switch (IsError) { case false when _resultValue == null: await nullValue.Invoke(); return; case true: await failure.Invoke(_resultError!); return; default: await success(_resultValue!); break; } } public async Task MatchAsync(Func success, Func failure) { switch (IsError) { case true: await failure.Invoke(_resultError!); return; default: await success(_resultValue!); break; } } public async Task MatchAsync(Func success) { if (IsError) { return; } await success(_resultValue!); } }