diff --git a/src/RunnethOverStudio.AppToolkit/Core/ProcessResult.cs b/src/RunnethOverStudio.AppToolkit/Core/ProcessResult.cs
index 56830a1..ac7146f 100644
--- a/src/RunnethOverStudio.AppToolkit/Core/ProcessResult.cs
+++ b/src/RunnethOverStudio.AppToolkit/Core/ProcessResult.cs
@@ -8,11 +8,24 @@
namespace RunnethOverStudio.AppToolkit.Core;
///
-/// Represents a result of an operation which can be the actual result or exception.
+/// Represents the result of an operation that either produced a value or intentionally captured an exception.
///
-/// The type of the value stored in the Result.
+/// The type of the value stored in the result.
///
-/// Heavily inspired by the Result type from .NEXT (dotNext).
+///
+/// Use when an exception is part of the operation's explicit result contract, such as
+/// when a boundary deliberately captures an exception so the caller can inspect, forward, or defer it.
+///
+///
+/// Expected application or domain outcomes should not be converted into exceptions merely to fit this type.
+/// When callers are expected to branch on a known set of non-exceptional failure states, use
+/// instead. Unexpected or exceptional failures should normally continue to
+/// propagate as exceptions unless the boundary has a concrete reason to capture them.
+///
+///
+/// Heavily inspired by the Result type
+/// from .NEXT (dotNext).
+///
///
[Serializable]
public class ProcessResult
@@ -27,15 +40,19 @@ public class ProcessResult
public ProcessResult(T value) => this._value = value;
///
- /// Initializes a new unsuccessful result.
+ /// Initializes a new unsuccessful result containing a captured exception.
///
- /// The exception representing error. Cannot be .
+ /// The exception representing the failure. Cannot be .
+ ///
+ /// This constructor is intended for failures that are exceptional in nature. Prefer
+ /// for expected failure states that are part of normal application flow.
+ ///
public ProcessResult(Exception error) : this(ExceptionDispatchInfo.Capture(error)) { }
///
- /// Extracts the actual result.
+ /// Extracts the successful value or rethrows the captured exception.
///
- /// This result is not successful.
+ /// This result contains a captured exception.
public T Value
{
get
@@ -46,20 +63,20 @@ public T Value
}
///
- /// Gets the value if present; otherwise return default value.
+ /// Gets the value if present; otherwise returns the default value.
///
- /// The value, if present, otherwise default.
+ /// The value, if present; otherwise, default.
public T? ValueOrDefault => _value;
///
- /// Gets exception associated with this result.
+ /// Gets the exception associated with this result, or when successful.
///
public Exception? Error => _exception?.SourceException;
///
- /// Indicates that the result is successful.
+ /// Indicates whether the result contains a successful value rather than a captured exception.
///
- /// if this result is successful; if this result represents exception.
+ /// when successful; otherwise, .
[MemberNotNullWhen(false, nameof(Error))]
public bool IsSuccessful => _exception is null;
@@ -79,12 +96,16 @@ public T Value
/// Creates a failed containing the specified exception.
///
/// The exception representing the failure. Cannot be .
- /// A representing a failed operation.
+ /// A representing an exceptional failure.
+ ///
+ /// Prefer when the failure is an expected outcome that callers should handle
+ /// without exception semantics.
+ ///
public static ProcessResult Failure(Exception error) => new(error);
///
/// Logs a failure message and exception using the specified logger and log level, so long as logging is enabled.
- /// Then returns a failed containing a new exception with the provided message and
+ /// Then returns a failed containing a new exception with the provided message and
/// the original exception as its inner exception.
///
/// The message to log and to use as the new exception's message.
@@ -109,7 +130,7 @@ public static ProcessResult LogAndForwardException(string message, Exception
///
/// The result to evaluate.
///
- /// true if the result is successful; otherwise, false.
+ /// if the result is successful; otherwise, .
///
public static implicit operator bool(ProcessResult result) => result.IsSuccessful;
diff --git a/src/RunnethOverStudio.AppToolkit/Core/ProcessResultOfError.cs b/src/RunnethOverStudio.AppToolkit/Core/ProcessResultOfError.cs
new file mode 100644
index 0000000..e90e47f
--- /dev/null
+++ b/src/RunnethOverStudio.AppToolkit/Core/ProcessResultOfError.cs
@@ -0,0 +1,165 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+
+namespace RunnethOverStudio.AppToolkit.Core;
+
+///
+/// Represents the result of an operation that either produced a value or an expected, explicitly modeled error.
+///
+/// The type of the successful value stored in the result.
+///
+/// An enumeration describing expected failure states. The default enumeration value is reserved to represent success.
+///
+///
+///
+/// Use when failure is an anticipated part of normal application or domain flow
+/// and callers are expected to branch on a known error state. Examples include a requested resource not being found,
+/// a destination no longer existing, or another explicitly modeled business outcome.
+///
+///
+/// This type is intentionally not an exception container. Unexpected or exceptional failures should normally propagate
+/// as exceptions. When a boundary deliberately needs to capture an exception as part of its result contract, use
+/// instead.
+///
+///
+/// The default value of is reserved for success. Define a neutral member such as
+/// None = 0 when naming that state improves readability, and use non-default values for actual errors.
+///
+///
+/// Heavily inspired by the Result type
+/// from .NEXT (dotNext).
+///
+///
+[Serializable]
+public class ProcessResult where TError : struct, Enum
+{
+ private readonly T _value;
+ private readonly TError _error;
+
+ ///
+ /// Initializes a new successful result.
+ ///
+ /// The value to be stored as the successful result.
+ public ProcessResult(T value)
+ {
+ _value = value;
+ _error = default;
+ }
+
+ ///
+ /// Initializes a new unsuccessful result containing an expected error.
+ ///
+ /// The expected error describing why the operation was unsuccessful.
+ ///
+ /// is the default value of , which is reserved for success.
+ ///
+ public ProcessResult(TError error)
+ {
+ if (EqualityComparer.Default.Equals(error, default))
+ {
+ throw new ArgumentOutOfRangeException(nameof(error), "The default error value is reserved for successful results.");
+ }
+
+ _value = default!;
+ _error = error;
+ }
+
+ ///
+ /// Extracts the successful value.
+ ///
+ ///
+ /// This result contains an expected error rather than a successful value.
+ ///
+ public T Value
+ {
+ get
+ {
+ Validate();
+ return _value;
+ }
+ }
+
+ ///
+ /// Gets the successful value when present; otherwise returns the default value.
+ ///
+ /// The successful value, if present; otherwise, default.
+ public T? ValueOrDefault => IsSuccessful ? _value : default;
+
+ ///
+ /// Gets the expected error associated with the result.
+ ///
+ ///
+ /// The default value represents success and is not an error. Check before interpreting
+ /// this property as a failure state.
+ ///
+ public TError Error => _error;
+
+ ///
+ /// Indicates whether the result contains a successful value rather than an expected error.
+ ///
+ /// when successful; otherwise, .
+ public bool IsSuccessful => EqualityComparer.Default.Equals(_error, default);
+
+ ///
+ /// Returns a string that represents the current result, indicating success or failure and the associated value or error.
+ ///
+ public override string ToString() => IsSuccessful ? $"Success({_value})" : $"Failure({_error})";
+
+ ///
+ /// Creates a successful containing the specified value.
+ ///
+ /// The value to store in the successful result.
+ /// A representing a successful operation.
+ public static ProcessResult Success(T value) => new(value);
+
+ ///
+ /// Creates a failed containing the specified expected error.
+ ///
+ /// The expected error describing why the operation was unsuccessful.
+ /// A representing an expected failure.
+ ///
+ /// is the default value of , which is reserved for success.
+ ///
+ public static ProcessResult Failure(TError error) => new(error);
+
+ ///
+ /// Attempts to extract the successful value.
+ ///
+ /// The successful value when present; otherwise, the default value.
+ /// when successful; otherwise, .
+ public bool TryGet([MaybeNullWhen(false)] out T value)
+ {
+ value = _value;
+ return IsSuccessful;
+ }
+
+ ///
+ /// Defines an implicit conversion from to .
+ ///
+ /// The result to evaluate.
+ ///
+ /// if the result is successful; otherwise, .
+ ///
+ public static implicit operator bool(ProcessResult result) => result.IsSuccessful;
+
+ ///
+ /// Defines an explicit conversion from to the underlying value of type .
+ ///
+ /// The result to extract the value from.
+ /// The value contained in the result if it is successful.
+ ///
+ /// Thrown if the result is not successful and an attempt is made to extract the value.
+ ///
+ public static explicit operator T(ProcessResult result) => result.Value;
+
+ [StackTraceHidden]
+ private void Validate()
+ {
+ if (!IsSuccessful)
+ {
+ throw new InvalidOperationException($"The process result is unsuccessful with error '{_error}'.");
+ }
+ }
+}