Created
February 17, 2019 15:54
-
-
Save jermdavis/0b27c613e7e252e4a1cd68fc530b7d77 to your computer and use it in GitHub Desktop.
An attempt at an error-friendly pipeline
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
public struct Either<SUCCESS, FAILURE> | |
{ | |
private readonly bool _isSuccess; | |
private readonly SUCCESS _success; | |
private readonly FAILURE _failure; | |
public bool IsSuccess => _isSuccess; | |
public bool IsFailure => !IsSuccess; | |
public SUCCESS SuccessValue => _success; | |
public FAILURE FailureValue => _failure; | |
public Either(SUCCESS value) | |
{ | |
_isSuccess = true; | |
_success = value; | |
_failure = default(FAILURE); | |
} | |
public Either(FAILURE value) | |
{ | |
_isSuccess = false; | |
_success = default(SUCCESS); | |
_failure = value; | |
} | |
public override string ToString() | |
{ | |
if(_isSuccess) | |
{ | |
return _success.ToString(); | |
} | |
else | |
{ | |
return _failure.ToString(); | |
} | |
} | |
public static implicit operator Either<SUCCESS, FAILURE>(FAILURE value) => new Either<SUCCESS, FAILURE>(value); | |
public static implicit operator Either<SUCCESS, FAILURE>(SUCCESS value) => new Either<SUCCESS, FAILURE>(value); | |
public static implicit operator SUCCESS(Either<SUCCESS, FAILURE> value) => value._success; | |
public static implicit operator FAILURE(Either<SUCCESS, FAILURE> value) => value._failure; | |
public T Match<T>(Func<SUCCESS, T> successFn, Func<FAILURE, T> failureFn) | |
{ | |
if(_isSuccess) | |
{ | |
return successFn(_success); | |
} | |
else | |
{ | |
return failureFn(_failure); | |
} | |
} | |
} |
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
public interface IErrorAwarePipelineStep<INPUT, OUTPUT, ERROR> | |
{ | |
Either<OUTPUT, ERROR> Process(Either<INPUT, ERROR> input); | |
} | |
public interface IErrorAwarePipeline<INPUT, OUTPUT, ERROR> : IErrorAwarePipelineStep<INPUT, OUTPUT, ERROR> | |
{ | |
Func<Either<INPUT, ERROR>, Either<OUTPUT, ERROR>> PipelineSteps { get; } | |
} | |
public static class ErrorAwarePipelineStepExtensions | |
{ | |
public static Either<OUTPUT, ERROR> ErrorAwareStep<INPUT, OUTPUT, ERROR>(this Either<INPUT, ERROR> input, IErrorAwarePipelineStep<INPUT, OUTPUT, ERROR> step) | |
{ | |
return step.Process(input); | |
} | |
} | |
public abstract class ErrorAwarePipelineStep<INPUT, OUTPUT, ERROR> : IErrorAwarePipelineStep<INPUT, OUTPUT, ERROR> | |
{ | |
public abstract Either<OUTPUT, ERROR> ProcessSuccessInput(Either<INPUT, ERROR> input); | |
public virtual Either<OUTPUT, ERROR> ProcessErrorInput(Either<INPUT, ERROR> input) | |
{ | |
return input.FailureValue; | |
} | |
public Either<OUTPUT, ERROR> Process(Either<INPUT, ERROR> input) | |
{ | |
if (input.IsSuccess) | |
{ | |
return ProcessSuccessInput(input); | |
} | |
else | |
{ | |
return ProcessErrorInput(input); | |
} | |
} | |
} | |
public abstract class ErrorAwarePipeline<INPUT, OUTPUT, ERROR> : IErrorAwarePipeline<INPUT, OUTPUT, ERROR> | |
{ | |
public Func<Either<INPUT, ERROR>, Either<OUTPUT, ERROR>> PipelineSteps { get; protected set; } | |
public Either<OUTPUT, ERROR> Process(Either<INPUT, ERROR> input) | |
{ | |
return PipelineSteps(input); | |
} | |
} |
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
public class ErrorAwareStringToIntStep : ErrorAwarePipelineStep<string, int, Exception> | |
{ | |
public override Either<int, Exception> ProcessSuccessInput(Either<string, Exception> input) | |
{ | |
int value; | |
if (int.TryParse(input.SuccessValue, out value)) | |
{ | |
return value; | |
} | |
else | |
{ | |
return new Exception("Can't parse string to integer"); | |
} | |
} | |
} | |
public class ErrorAwareMultiplyStep : ErrorAwarePipelineStep<int, float, Exception> | |
{ | |
public override Either<float, Exception> ProcessSuccessInput(Either<int, Exception> input) | |
{ | |
return input * 2.1f; | |
} | |
} | |
public class ErrorStateChangeStep : ErrorAwarePipelineStep<float, float, Exception> | |
{ | |
public override Either<float, Exception> ProcessSuccessInput(Either<float, Exception> input) | |
{ | |
return input; | |
} | |
public override Either<float, Exception> ProcessErrorInput(Either<float, Exception> input) | |
{ | |
return new Exception("This is a custom exception", input.FailureValue); | |
} | |
} | |
public class ExampleErrorAwarePipeline : ErrorAwarePipeline<string, float, Exception> | |
{ | |
public ExampleErrorAwarePipeline() | |
{ | |
PipelineSteps = input => input | |
.ErrorAwareStep(new ErrorAwareStringToIntStep()) | |
.ErrorAwareStep(new ErrorAwareMultiplyStep()) | |
.ErrorAwareStep(new ErrorStateChangeStep()); | |
} | |
} |
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
public class Program | |
{ | |
private static IErrorAwarePipeline<string, float, Exception> _pipeline = new ExampleErrorAwarePipeline(); | |
private static void runPipeline(string input) | |
{ | |
Console.WriteLine("Running pipeline:"); | |
var result = _pipeline | |
.Process(input) | |
.Match( | |
f => $"Success: Transformed {input}[{input.GetType().Name}] to {f}[{f.GetType().Name}]", | |
f => $"Error: Transformed {input}[{input.GetType().Name}] to {f}[{f.GetType().Name}]" | |
); | |
Console.WriteLine(result); | |
Console.WriteLine(); | |
} | |
private static void Main(string[] args) | |
{ | |
runPipeline("27"); | |
runPipeline("0"); | |
runPipeline("0.1"); | |
runPipeline("hello"); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment