Skip to content

Add exception-safe Try / TryAsync mapping helpers for FailOr<T>#5

Merged
mark-pro merged 3 commits intomainfrom
copilot/add-try-tryasync-mapping-helpers
Mar 8, 2026
Merged

Add exception-safe Try / TryAsync mapping helpers for FailOr<T>#5
mark-pro merged 3 commits intomainfrom
copilot/add-try-tryasync-mapping-helpers

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 8, 2026

This adds success-only mapping helpers that preserve FailOr short-circuiting while turning mapping exceptions into failures. The new APIs cover both direct and Task<FailOr<T>> flows, with default Failure.Exceptional(...) conversion and opt-in custom exception projection.

  • API surface

    • Added src/FailOr/FailOrT.Try.cs
    • Introduced direct overloads for FailOr<TSource>:
      • Try<TResult>(Func<TSource, TResult> map)
      • Try<TResult>(Func<TSource, TResult> map, Func<Exception, FailOr<TResult>> onException)
      • TryAsync<TResult>(Func<TSource, Task<TResult>> mapAsync)
      • TryAsync<TResult>(Func<TSource, Task<TResult>> mapAsync, Func<Exception, FailOr<TResult>> onException)
    • Introduced matching lifted overloads for Task<FailOr<TSource>>
  • Behavior

    • Successful sources map normally when the delegate completes without throwing
    • Failed sources short-circuit and preserve the original failures unchanged
    • Overloads without onException convert thrown exceptions to FailOr.Fail<TResult>(Failure.Exceptional(exception))
    • Overloads with onException project the exception into a repo-native FailOr<TResult>
    • Async overloads preserve null-task validation and continue throwing ArgumentNullException when a mapping delegate returns null
  • Coverage

    • Added focused tests for:
      • successful direct and async mappings
      • failed-source short-circuiting
      • default exceptional failure conversion
      • custom onException mapping
      • null map / mapAsync / onException handling
      • null returned task handling for async variants
      • parity between direct and lifted overloads
  • Docs

    • Added XML documentation with examples and exception details for each public overload
    • Updated README.md with Try examples for default and custom exception mapping
    • Updated docs/api-reference.md to document the new APIs and their lifted counterparts
using FailOr;

var parsed = FailOr.Success("42")
    .Try(int.Parse);

var fallback = FailOr.Success("42x")
    .Try(
        int.Parse,
        exception => Failure.General($"Mapping failed: {exception.Message}"));

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • https://api.github.com/graphql
    • Triggering command: /usr/bin/gh gh auth status (http block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

This section details on the original issue you should resolve

<issue_title>Add Try / TryAsync exception-safe mapping helpers</issue_title>
<issue_description>Add exception-safe mapping helpers in a new src/FailOr/FailOrT.Try.cs file.

The goal is to provide a success-only map that catches exceptions thrown during mapping and converts them to Failure.Exceptional(...) by default, while also supporting overloads that accept a second callback to transform the thrown exception into a repo-native result, for example Try(x => x, e => Failure.General("mapping failed")). A checklist item in this issue is only complete when the implementation, relevant tests, and required documentation updates for that item land together in the same change.

API

  • Create new file src/FailOr/FailOrT.Try.cs
  • FailOr<TResult> Try<TResult>(Func<TSource, TResult> map) for FailOr<TSource>
  • FailOr<TResult> Try<TResult>(Func<TSource, TResult> map, Func<Exception, FailOr<TResult>> onException) for FailOr<TSource>
  • Task<FailOr<TResult>> TryAsync<TResult>(Func<TSource, Task<TResult>> mapAsync) for FailOr<TSource>
  • Task<FailOr<TResult>> TryAsync<TResult>(Func<TSource, Task<TResult>> mapAsync, Func<Exception, FailOr<TResult>> onException) for FailOr<TSource>
  • Task<FailOr<TResult>> Try<TResult>(Func<TSource, TResult> map) for Task<FailOr<TSource>>
  • Task<FailOr<TResult>> Try<TResult>(Func<TSource, TResult> map, Func<Exception, FailOr<TResult>> onException) for Task<FailOr<TSource>>
  • Task<FailOr<TResult>> TryAsync<TResult>(Func<TSource, Task<TResult>> mapAsync) for Task<FailOr<TSource>>
  • Task<FailOr<TResult>> TryAsync<TResult>(Func<TSource, Task<TResult>> mapAsync, Func<Exception, FailOr<TResult>> onException) for Task<FailOr<TSource>>
  • Do not add bind-style overloads such as Func<TSource, FailOr<TResult>> or Func<TSource, Task<FailOr<TResult>>>

Expected Behavior

  • Successful sources map to a new successful result when the delegate completes without throwing
  • Failed sources short-circuit and preserve the original failures unchanged
  • Overloads without an exception callback convert thrown exceptions to FailOr.Fail<TResult>(Failure.Exceptional(exception))
  • Overloads with onException invoke that callback with the thrown exception and return the projected FailOr<TResult>
  • The exception-mapping callback is available for every direct and lifted sync/async arity listed above
  • Null mapping delegates throw ArgumentNullException
  • Null onException delegates throw ArgumentNullException
  • Async overloads throw ArgumentNullException when the mapping delegate returns null
  • Lifted Task<FailOr<TSource>> overloads match the direct overload behavior

Documentation

  • XML documentation with exceptions and example for each public function
  • README.md updated with Try examples for both the default Failure.Exceptional(...) behavior and a custom onException mapping
  • docs/api-reference.md updated

Verification

  • Direct overload tests for successful mapping
  • Direct overload tests for failed-source short-circuiting
  • Direct overload tests for default exception-to-Failure.Exceptional conversion
  • Direct overload tests for custom onException mapping
  • Direct overload tests for null mapping delegate handling
  • Direct overload tests for null onException handling
  • Direct async overload tests for null returned task handling
  • Lifted overload parity tests against direct behavior
  • dotnet csharpier format run on changed files
  • dotnet test --solution fail-or.slnx passes
    </issue_description>

Comments on the Issue (you are @copilot in this section)


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI and others added 2 commits March 8, 2026 13:20
Co-authored-by: mark-pro <20671988+mark-pro@users.noreply.github.com>
Co-authored-by: mark-pro <20671988+mark-pro@users.noreply.github.com>
Copilot AI changed the title [WIP] Add Try / TryAsync exception-safe mapping helpers Add exception-safe Try / TryAsync mapping helpers for FailOr<T> Mar 8, 2026
@mark-pro mark-pro marked this pull request as ready for review March 8, 2026 13:48
@mark-pro mark-pro merged commit 48d5653 into main Mar 8, 2026
2 checks passed
@mark-pro mark-pro deleted the copilot/add-try-tryasync-mapping-helpers branch March 9, 2026 02:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Try / TryAsync exception-safe mapping helpers

2 participants