-
Notifications
You must be signed in to change notification settings - Fork 305
Expand file tree
/
Copy pathResult.cs
More file actions
179 lines (157 loc) · 7.6 KB
/
Result.cs
File metadata and controls
179 lines (157 loc) · 7.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
namespace CleanArchitecture.Blazor.Application.Common.Models;
/// <summary>
/// Represents the result of an operation.
/// </summary>
public class Result : IResult
{
/// <summary>
/// Protected constructor to initialize the result.
/// </summary>
/// <param name="succeeded">Indicates whether the operation succeeded.</param>
/// <param name="errors">A collection of error messages.</param>
protected Result(bool succeeded, IEnumerable<string>? errors)
{
Succeeded = succeeded;
Errors = errors?.ToArray() ?? Array.Empty<string>();
}
/// <summary>
/// Indicates whether the operation succeeded.
/// </summary>
public bool Succeeded { get; init; }
/// <summary>
/// An array of error messages.
/// </summary>
public string[] Errors { get; init; }
/// <summary>
/// Gets a concatenated string of error messages, separated by commas.
/// </summary>
public string ErrorMessage => string.Join(", ", Errors);
/// <summary>
/// Creates a successful <see cref="Result"/> instance.
/// </summary>
public static Result Success() => new Result(true, Array.Empty<string>());
/// <summary>
/// Asynchronously creates a successful <see cref="Result"/> instance.
/// </summary>
public static Task<Result> SuccessAsync() => Task.FromResult(Success());
/// <summary>
/// Creates a failed <see cref="Result"/> instance.
/// </summary>
/// <param name="errors">Error messages.</param>
public static Result Failure(params string[] errors) => new Result(false, errors);
/// <summary>
/// Asynchronously creates a failed <see cref="Result"/> instance.
/// </summary>
/// <param name="errors">Error messages.</param>
public static Task<Result> FailureAsync(params string[] errors) => Task.FromResult(Failure(errors));
/// <summary>
/// Executes the appropriate function based on whether the operation succeeded.
/// </summary>
/// <typeparam name="TResult">The return type.</typeparam>
/// <param name="onSuccess">Function to execute if the operation succeeded.</param>
/// <param name="onFailure">Function to execute if the operation failed, with an error message.</param>
public TResult Match<TResult>(Func<TResult> onSuccess, Func<string, TResult> onFailure)
=> Succeeded ? onSuccess() : onFailure(ErrorMessage);
/// <summary>
/// Asynchronously executes the appropriate function based on whether the operation succeeded.
/// </summary>
/// <typeparam name="TResult">The return type.</typeparam>
/// <param name="onSuccess">Asynchronous function to execute if the operation succeeded.</param>
/// <param name="onFailure">Asynchronous function to execute if the operation failed, with an error message.</param>
public Task<TResult> MatchAsync<TResult>(Func<Task<TResult>> onSuccess, Func<string, Task<TResult>> onFailure)
=> Succeeded ? onSuccess() : onFailure(ErrorMessage);
}
/// <summary>
/// Represents the result of an operation that includes data.
/// </summary>
/// <typeparam name="T">The type of the data.</typeparam>
public class Result<T> : Result, IResult<T>
{
/// <summary>
/// The data contained in the result.
/// </summary>
public T? Data { get; init; }
/// <summary>
/// Protected constructor to initialize a result with data.
/// </summary>
/// <param name="succeeded">Indicates whether the operation succeeded.</param>
/// <param name="errors">A collection of error messages.</param>
/// <param name="data">The data returned from the operation.</param>
protected Result(bool succeeded, IEnumerable<string>? errors, T? data)
: base(succeeded, errors)
{
Data = data;
}
/// <summary>
/// Creates a successful <see cref="Result{T}"/> instance with the specified data.
/// </summary>
/// <param name="data">The data to include in the result.</param>
public static Result<T> Success(T data) => new Result<T>(true, Array.Empty<string>(), data);
/// <summary>
/// Asynchronously creates a successful <see cref="Result{T}"/> instance with the specified data.
/// </summary>
/// <param name="data">The data to include in the result.</param>
public static Task<Result<T>> SuccessAsync(T data) => Task.FromResult(Success(data));
/// <summary>
/// Creates a failed <see cref="Result{T}"/> instance.
/// </summary>
/// <param name="errors">Error messages.</param>
public static new Result<T> Failure(params string[] errors) => new Result<T>(false, errors, default);
/// <summary>
/// Asynchronously creates a failed <see cref="Result{T}"/> instance.
/// </summary>
/// <param name="errors">Error messages.</param>
public static new Task<Result<T>> FailureAsync(params string[] errors) => Task.FromResult(Failure(errors));
/// <summary>
/// Executes the appropriate action based on whether the operation succeeded.
/// </summary>
/// <param name="onSuccess">Action to execute if the operation succeeded, with the data.</param>
/// <param name="onFailure">Action to execute if the operation failed, with an error message.</param>
public void Match(Action<T> onSuccess, Action<string> onFailure)
{
if (Succeeded)
onSuccess(Data!);
else
onFailure(ErrorMessage);
}
/// <summary>
/// Asynchronously executes the appropriate action based on whether the operation succeeded.
/// </summary>
/// <param name="onSuccess">Asynchronous action to execute if the operation succeeded, with the data.</param>
/// <param name="onFailure">Asynchronous action to execute if the operation failed, with an error message.</param>
public async Task MatchAsync(Func<T, Task> onSuccess, Func<string, Task> onFailure)
{
if (Succeeded)
await onSuccess(Data!);
else
await onFailure(ErrorMessage);
}
/// <summary>
/// Maps the data contained in the result to a new type.
/// </summary>
/// <typeparam name="TResult">The type of the mapped data.</typeparam>
/// <param name="map">The mapping function.</param>
public Result<TResult> Map<TResult>(Func<T, TResult> map)
=> Succeeded ? Result<TResult>.Success(map(Data!)) : Result<TResult>.Failure(Errors);
/// <summary>
/// Asynchronously maps the data contained in the result to a new type.
/// </summary>
/// <typeparam name="TResult">The type of the mapped data.</typeparam>
/// <param name="map">The asynchronous mapping function.</param>
public async Task<Result<TResult>> MapAsync<TResult>(Func<T, Task<TResult>> map)
=> Succeeded ? Result<TResult>.Success(await map(Data!)) : await Result<TResult>.FailureAsync(Errors);
/// <summary>
/// Binds the result to another result, allowing for chaining operations.
/// </summary>
/// <typeparam name="TResult">The type of the data in the resulting result.</typeparam>
/// <param name="bind">The binding function that returns a new result.</param>
public Result<TResult> Bind<TResult>(Func<T, Result<TResult>> bind)
=> Succeeded ? bind(Data!) : Result<TResult>.Failure(Errors);
/// <summary>
/// Asynchronously binds the result to another result, allowing for chaining operations.
/// </summary>
/// <typeparam name="TResult">The type of the data in the resulting result.</typeparam>
/// <param name="bind">The asynchronous binding function that returns a new result.</param>
public async Task<Result<TResult>> BindAsync<TResult>(Func<T, Task<Result<TResult>>> bind)
=> Succeeded ? await bind(Data!) : await Result<TResult>.FailureAsync(Errors);
}