// Copyright (c) 2025 GeWuYou
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using GFramework.Core.Functional;
namespace GFramework.Core.functional.result;
///
/// Result 类型的扩展方法,提供函数式编程支持
///
public static class ResultExtensions
{
#region 聚合
///
/// 将多个结果合并,全部成功则返回值列表,遇到第一个失败即短路返回
///
///
///
/// var combined = new[]
/// {
/// Result<int>.Succeed(1),
/// Result<int>.Succeed(2),
/// Result<int>.Succeed(3)
/// }.Combine(); // Result<List<int>> [1, 2, 3]
///
///
public static Result> Combine(
this IEnumerable> results)
{
ArgumentNullException.ThrowIfNull(results);
var values = new List();
foreach (var result in results)
{
if (result.IsFaulted)
return Result>.Fail(result.Exception);
if (result.IsSuccess)
values.Add(result.Match(succ: v => v, fail: _ => throw new InvalidOperationException()));
}
return Result>.Succeed(values);
}
#endregion
#region 变换
///
/// 映射成功值到新类型
///
///
///
/// var result = Result<int>.Succeed(42);
/// var mapped = result.Map(x => x.ToString()); // Result<string> "42"
///
///
public static Result Map(
this Result result,
Func mapper)
{
ArgumentNullException.ThrowIfNull(mapper);
return result.Map(mapper);
}
///
/// 绑定操作,将结果链式转换
///
///
///
/// var result = Result<int>.Succeed(42);
/// var bound = result.Bind(x => x > 0
/// ? Result<string>.Succeed(x.ToString())
/// : Result<string>.Fail(new ArgumentException("Value must be positive")));
///
///
public static Result Bind(
this Result result,
Func> binder)
{
ArgumentNullException.ThrowIfNull(binder);
return result.Bind(binder);
}
///
/// 异步绑定操作,将结果链式转换为异步结果
///
///
///
/// var result = Result<int>.Succeed(42);
/// var bound = await result.BindAsync(async x =>
/// await GetUserAsync(x) is User user
/// ? Result<User>.Succeed(user)
/// : Result<User>.Fail(new Exception("User not found")));
///
///
public static async Task> BindAsync(
this Result result,
Func>> binder)
{
ArgumentNullException.ThrowIfNull(binder);
return result.IsSuccess
? await binder(result.Match(succ: v => v, fail: _ => throw new InvalidOperationException()))
: Result.Fail(result.Exception);
}
#endregion
#region 副作用
///
/// 成功时执行副作用,返回原始结果(可链式调用)
///
///
///
/// Result<int>.Succeed(42)
/// .OnSuccess(x => Console.WriteLine($"Value: {x}"))
/// .OnFailure(ex => Console.WriteLine($"Error: {ex.Message}"));
///
///
public static Result OnSuccess(
this Result result,
Action action)
{
ArgumentNullException.ThrowIfNull(action);
result.IfSucc(action);
return result;
}
///
/// 失败时执行副作用,返回原始结果(可链式调用)
///
public static Result OnFailure(
this Result result,
Action action)
{
ArgumentNullException.ThrowIfNull(action);
result.IfFail(action);
return result;
}
#endregion
#region 验证
///
/// 确保成功值满足条件,否则转换为失败
///
///
///
/// var result = Result<int>.Succeed(42)
/// .Ensure(x => x > 0, "Value must be positive")
/// .Ensure(x => x < 100, "Value must be less than 100");
///
///
public static Result Ensure(
this Result result,
Func predicate,
string errorMessage)
{
ArgumentNullException.ThrowIfNull(predicate);
ArgumentException.ThrowIfNullOrWhiteSpace(errorMessage);
if (result.IsFaulted) return result;
return result.Match(
succ: value => predicate(value)
? result
: Result.Fail(new ArgumentException(errorMessage)),
fail: _ => result
);
}
///
/// Ensure 的异常重载,可以传入更具体的异常类型
///
public static Result Ensure(
this Result result,
Func predicate,
Func exceptionFactory)
{
ArgumentNullException.ThrowIfNull(predicate);
ArgumentNullException.ThrowIfNull(exceptionFactory);
if (result.IsFaulted) return result;
return result.Match(
succ: value => predicate(value)
? result
: Result.Fail(exceptionFactory(value)),
fail: _ => result
);
}
#endregion
#region 安全执行
///
/// 安全执行同步委托,自动捕获异常为 Result
///
///
///
/// var result = ResultExtensions.Try(() => int.Parse("42"));
///
///
public static Result Try(Func func)
{
ArgumentNullException.ThrowIfNull(func);
return Result.Try(func);
}
///
/// 安全执行异步委托,自动捕获异常为 Result
///
///
///
/// var result = await ResultExtensions.TryAsync(async () => await GetDataAsync());
///
///
public static async Task> TryAsync(Func> func)
{
ArgumentNullException.ThrowIfNull(func);
try
{
return Result.Succeed(await func());
}
catch (Exception ex)
{
return Result.Fail(ex);
}
}
#endregion
#region 类型转换
///
/// 成功时返回值,失败时返回 null(值类型)
///
public static T? ToNullable(this Result result) where T : struct =>
result.IsSuccess
? result.Match(succ: v => (T?)v, fail: _ => null)
: null;
///
/// 将可空引用类型转换为 Result
///
public static Result ToResult(
this T? value,
string errorMessage = "Value is null") where T : class =>
value is not null
? Result.Succeed(value)
: Result.Fail(new ArgumentNullException(errorMessage));
///
/// 将可空值类型转换为 Result
///
public static Result ToResult(
this T? value,
string errorMessage = "Value is null") where T : struct =>
value.HasValue
? Result.Succeed(value.Value)
: Result.Fail(new ArgumentNullException(errorMessage));
#endregion
}