### Install OneOf.SourceGenerator Package Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/api-reference-source-generator.md Use this command to install the OneOf.SourceGenerator NuGet package. Requires C# 9.0+. ```powershell Install-Package OneOf.SourceGenerator ``` -------------------------------- ### Install OneOf Package Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/README.md Add the OneOf NuGet package to your .NET project using the dotnet CLI. ```bash dotnet add package OneOf ``` -------------------------------- ### Install OneOf Extended Arities Package Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/package-overview.md Add the OneOf.Extended package for extended arities. ```bash dotnet add package OneOf.Extended ``` -------------------------------- ### Install OneOf Source Generator Package Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/package-overview.md Add the OneOf.SourceGenerator package for source generation capabilities. ```bash dotnet add package OneOf.SourceGenerator ``` -------------------------------- ### Instantiate Success Type Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/types.md Example of creating a Success type and using it within a OneOf. ```csharp var success = new Success(42); var result = new OneOf, Error>(success); ``` -------------------------------- ### Instantiate Result Type Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/types.md Example of creating a Result type and using it within a OneOf. ```csharp var result = new Result>(users); var outcome = new OneOf>, Error>(result); ``` -------------------------------- ### Usage Example for Generated ApiResponse Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/api-reference-source-generator.md Demonstrates implicit conversion, pattern matching with Match, and explicit downcasting for the generated ApiResponse class. ```csharp // Implicit conversion ApiResponse success = new Data { Content = "Hello" }; ApiResponse notFound = new NotFound(); ApiResponse error = new Error { Message = "Server error" }; // Pattern matching string response = success.Match( data => $"Data: {data.Content}", notFound => "Not Found", error => $"Error: {error.Message}" ); // Explicit downcast var data = (Data)success; ``` -------------------------------- ### Boxing Behavior Example Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/api-reference-iooneof.md Demonstrates boxing when accessing the Value property of IOneOf for value types, and no boxing for reference types. ```csharp OneOf value = 42; IOneOf iface = value; object obj = iface.Value; // int 42 is boxed here OneOf value2 = "hello"; IOneOf iface2 = value2; object obj2 = iface2.Value; // No boxing - already object ``` -------------------------------- ### Use YesOrNo Type Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/types.md Example demonstrating implicit conversion from boolean and using the Match method with the YesOrNo type. ```csharp YesOrNo response = true; // Implicit conversion string answer = response.Match( yes => "Affirmative", no => "Negative" ); ``` -------------------------------- ### Use YesNoOrMaybe Type Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/types.md Example demonstrating implicit conversion from nullable boolean and using the Match method with the YesNoOrMaybe type. ```csharp YesNoOrMaybe response = null; // Implicit: converts to Maybe string answer = response.Match( yes => "Yes", no => "No", maybe => "Maybe" ); ``` -------------------------------- ### OneOf Extension Methods Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/common-patterns.md Add convenience extension methods to OneOf types for common checks and value retrieval. This example shows methods for checking success and safely getting values. ```csharp public static class OneOfExtensions { public static bool IsSuccess(this OneOf result) => result.IsT0; public static T GetValueOrThrow(this OneOf result) => result.IsT0 ? result.AsT0 : throw result.AsT1; public static OneOf Flatten( this OneOf, Exception> nested) => nested.Match( inner => inner, error => error ); } // Usage var result = GetResult(); if (result.IsSuccess()) { var value = result.GetValueOrThrow(); } ``` -------------------------------- ### Example Usage of Implicit Operators Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/api-reference-source-generator.md Demonstrates how implicit conversion operators allow direct assignment of wrapped types to the generated OneOf class. ```csharp MyResult result = new Success(); // Implicit conversion via operator MyResult error = new Error(); // Implicit conversion via operator ``` -------------------------------- ### Register OneOf-Returning Service Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/integration-guide.md Register services that return OneOf types with your dependency injection container. This example shows registering a UserService. ```csharp services.AddScoped(); public interface IUserService { OneOf GetUser(int id); OneOf CreateUser(CreateUserRequest request); } public class UserService : IUserService { private readonly IUserRepository _repository; public UserService(IUserRepository repository) => _repository = repository; public OneOf GetUser(int id) => _repository.GetById(id); public OneOf CreateUser(CreateUserRequest request) => ValidateRequest(request) .Match( valid => _repository.Create(valid), error => error ); } ``` -------------------------------- ### Instantiate Error Type with Details Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/types.md Example of creating an Error type with details and using it within a OneOf. ```csharp var error = new Error("Operation failed"); var result = new OneOf>(error); ``` -------------------------------- ### Example of Implicit Conversion to OneOf Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/api-reference-oneof-struct.md Demonstrates how values of different types (string, int, bool) can be implicitly converted and assigned to a OneOf variable. ```csharp OneOf result = "hello"; // Implicitly converts to OneOf result = 42; // Can also hold int result = true; // Or bool ``` -------------------------------- ### Helper Methods on OneOfBase Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/api-reference-summary.md Provides an example of a helper method that can be added to OneOfBase derived classes to conditionally execute logic based on the contained value. ```APIDOC ## TResult IfSuccess(Func func) ### Description Executes a function if the OneOfBase instance is in a success state (holds a value of type T). Returns the result of the function or a default value if not in a success state. ### Parameters - **func** (Func) - Required - The function to execute if the instance is in a success state. ### Returns - TResult - The result of the executed function, or the default value of TResult if not successful. ``` -------------------------------- ### Type-Safe Configuration with OneOf Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/advanced-patterns.md Defines a configuration that can be one of several database types. Provides a method to get the appropriate database connector based on the configuration type. ```csharp public class DatabaseConfig : OneOfBase< SqlServerConfig, PostgresConfig, MongoDbConfig > { DatabaseConfig(OneOf _) : base(_) { } public static implicit operator DatabaseConfig(SqlServerConfig _) => new(_); public static implicit operator DatabaseConfig(PostgresConfig _) => new(_); public static implicit operator DatabaseConfig(MongoDbConfig _) => new(_); public IDbConnector GetConnector() => Match( sqlServer => new SqlServerConnector(sqlServer), postgres => new PostgresConnector(postgres), mongo => new MongoConnector(mongo) ); } // Usage DatabaseConfig config = GetConfigFromAppSettings(); var connector = config.GetConnector(); ``` -------------------------------- ### Generated OneOfBase boilerplate code Source: https://github.com/mcintyre321/oneof/blob/master/README.md Example of the code automatically generated by the OneOf.SourceGenerator for a OneOfBase class. It includes constructors, implicit/explicit conversions, and base method implementations. ```csharp public partial class StringOrNumber { public StringOrNumber(OneOf.OneOf _) : base(_) { } public static implicit operator StringOrNumber(System.String _) => new StringOrNumber(_); public static explicit operator System.String(StringOrNumber _) => _.AsT0; public static implicit operator StringOrNumber(System.Int32 _) => new StringOrNumber(_); public static explicit operator System.Int32(StringOrNumber _) => _.AsT1; } ``` -------------------------------- ### OneOf with Factory Pattern for Connections Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/integration-guide.md Demonstrates using OneOf with a factory pattern to create database connections, handling potential connection errors. ```csharp public interface IConnectionFactory { OneOf CreateConnection(ConnectionString connStr); } public class ConnectionFactory : IConnectionFactory { public OneOf CreateConnection( ConnectionString connStr) { try { var conn = new SqlConnection(connStr.Value); conn.Open(); return conn; } catch (SqlException ex) { return new ConnectionError { Message = ex.Message }; } } } // Usage services.AddScoped(); var factory = serviceProvider.GetRequiredService(); var result = factory.CreateConnection(connString); result.Switch( conn => UseConnection(conn), error => LogError(error) ); ``` -------------------------------- ### Basic Usage of OneOf Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/README.md Demonstrates how to define a method returning a OneOf type and how to consume its result using the Switch method for conditional execution. ```csharp using OneOf; public OneOf GetUser(int id) { var user = _repository.Find(id); return user ?? new NotFound(); } // Consuming the result var result = GetUser(123); result.Switch( user => Console.WriteLine($"Found: {user.Name}"), notFound => Console.WriteLine("User not found") ); ``` -------------------------------- ### Pattern Matching with OneOf Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/package-overview.md Shows how to use the Match method to handle all possible types within a OneOf instance. This ensures that all cases are considered. ```csharp string output = result.Match( str => $"String: {str}", num => $"Number: {num}" ); ``` -------------------------------- ### Core Method: Match (Exhaustive Pattern Matching) Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/api-reference-oneof-struct.md Execute handlers for each possible type and return a result. ```APIDOC ## Core Methods ### Match (Exhaustive Pattern Matching) ```csharp public TResult Match(Func f0, Func f1, Func f2) ``` Executes exactly one handler based on which type is held, returning a result of type `TResult`. All handlers must be provided. | Parameter | Type | Description | |-----------|------|-------------| | f0 | Func | Handler for T0 value | | f1 | Func | Handler for T1 value | | f2 | Func | Handler for T2 value | **Returns:** `TResult` — the result of the matched handler function. **Throws:** `InvalidOperationException` if any handler is null or if the internal index is invalid. **Example:** ```csharp OneOf value = "hello"; string result = value.Match( str => $"String: {str}", num => $"Number: {num}", flag => $"Flag: {flag}" ); // result = "String: hello" ``` ``` -------------------------------- ### Use TrueOrFalse with Implicit Conversion Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/types.md Demonstrates using the TrueOrFalse discriminated union with implicit conversion from a boolean value and matching the result. ```csharp TrueOrFalse flag = false; // Implicit conversion int result = flag.Match( t => 1, f => 0 ); ``` -------------------------------- ### File Organization Structure Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/README.md Illustrates the directory structure of the OneOf library's documentation files. Useful for understanding the layout of the project's documentation. ```text /output/ ├── README.md (this file) ├── package-overview.md ├── api-reference-summary.md ├── api-reference-oneof-struct.md ├── api-reference-oneof-base.md ├── api-reference-iooneof.md ├── api-reference-source-generator.md ├── types.md ├── errors.md ├── common-patterns.md ├── advanced-patterns.md └── integration-guide.md ``` -------------------------------- ### Example Usage of Explicit Operators Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/api-reference-source-generator.md Shows how to explicitly cast an instance of the generated OneOf class back to its original wrapped type. ```csharp MyResult result = new Success(); Success success = (Success)result; // Explicit cast back to Success ``` -------------------------------- ### Get String Representation of OneOf Instance Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/api-reference-oneof-struct.md Returns a string representation in the format 'TypeName: value'. Use this for debugging or logging OneOf values. ```csharp public override string ToString() { // ... implementation details ... } ``` ```csharp OneOf value = "test"; string str = value.ToString(); // "System.String: test" ``` -------------------------------- ### Get Index of Held Type Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/api-reference-oneof-struct.md Retrieves the zero-based index of the currently held type within the OneOf structure. Useful for determining which type is active. ```csharp public int Index { get; } ``` ```csharp OneOf value = "test"; int idx = value.Index; // Returns 0 ``` -------------------------------- ### Process Results with IOneOf Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/api-reference-iooneof.md Demonstrates how to process results using the IOneOf interface. This approach works with any OneOf type but sacrifices compile-time type safety. ```csharp void ProcessResult(IOneOf result) { Console.WriteLine($"Index: {result.Index}, Value: {result.Value}"); } // Works with any OneOf type ProcessResult((OneOf)42); ProcessResult((OneOf)true); ``` -------------------------------- ### Using TryPickT1 to handle NotFound or Error cases Source: https://github.com/mcintyre321/oneof/blob/master/README.md Demonstrates using TryPickT1 to check for specific error conditions (NotFound, Error) and extract values. The remainder is a OneOf of the remaining types. ```csharp IActionResult Get(string id) { OneOf thingOrNotFoundOrError = GetThingFromDb(string id); if (thingOrNotFoundOrError.TryPickT1(out NotFound notFound, out var thingOrError)) //thingOrError is a OneOf return StatusCode(404); if (thingOrError.TryPickT1(out var error, out var thing)) //note that thing is a Thing rather than a OneOf { _logger.LogError(error.Message); return StatusCode(500); } return Ok(thing); } ``` -------------------------------- ### Get Wrapped Value as Object Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/api-reference-oneof-struct.md Retrieves the currently held value as an object. This can be useful for generic handling, but be aware it throws an exception if the OneOf is in an invalid state. ```csharp public object Value { get; } ``` ```csharp OneOf value = "hello"; object obj = value.Value; // Returns (object)"hello" ``` -------------------------------- ### Core Method: Switch (Execute Without Return) Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/api-reference-oneof-struct.md Execute a specific action based on the contained type without returning a value. ```APIDOC ### Switch (Execute Without Return) ```csharp public void Switch(Action f0, Action f1, Action f2) ``` Executes exactly one handler based on which type is held. No return value. All handlers must be provided. | Parameter | Type | Description | |-----------|------|-------------| | f0 | Action | Handler for T0 value | | f1 | Action | Handler for T1 value | | f2 | Action | Handler for T2 value | **Example:** ```csharp OneOf value = 42; value.Switch( str => Console.WriteLine($"String: {str}"), num => Console.WriteLine($"Number: {num}"), flag => Console.WriteLine($"Flag: {flag}") ); // Prints: "Number: 42" ``` ``` -------------------------------- ### Define reusable OneOf types with additional members Source: https://github.com/mcintyre321/oneof/blob/master/README.md Inherit from OneOfBase to create custom OneOf types that can be reused. This example shows how to add custom methods like TryGetNumber. ```csharp public class StringOrNumber : OneOfBase { StringOrNumber(OneOf _) : base(_) { } // optionally, define implicit conversions // you could also make the constructor public public static implicit operator StringOrNumber(string _) => new StringOrNumber(_); public static implicit operator StringOrNumber(int _) => new StringOrNumber(_); public (bool isNumber, int number) TryGetNumber() { return Match( s => (int.TryParse(s, out var n), n), i => (true, i) ); } } StringOrNumber x = 5; Console.WriteLine(x.TryGetNumber().number); // prints 5 x = "5"; Console.WriteLine(x.TryGetNumber().number); // prints 5 x = "abcd"; Console.WriteLine(x.TryGetNumber().isNumber); // prints False ``` -------------------------------- ### Determining Type Index via IOneOf.Index Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/api-reference-iooneof.md Shows how to get the zero-based index of the currently held type within the OneOf generic parameters. This index can be used for dispatching logic. ```csharp IOneOf result = (OneOf)42; int index = result.Index; // Returns 1 (int is T1) // Can dispatch based on index switch (index) { case 0: Console.WriteLine("Holding T0"); break; case 1: Console.WriteLine("Holding T1"); break; case 2: Console.WriteLine("Holding T2"); break; } ``` -------------------------------- ### Mocking OneOf Results in Unit Tests Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/integration-guide.md Demonstrates how to mock services that return OneOf results for testing specific scenarios, such as payment failures. ```csharp [Test] public void ProcessOrder_WhenPaymentFails_ReturnsError() { var mockPaymentService = new Mock(); mockPaymentService .Setup(s => s.ProcessPayment(It.IsAny())) .Returns(new PaymentError { Message = "Card declined" }); var processor = new OrderProcessor(mockPaymentService.Object); var result = processor.ProcessOrder(order); result.AssertError(error => { Assert.AreEqual("Card declined", error.Message); }); } ``` -------------------------------- ### Create User with OneOf Return Type Source: https://github.com/mcintyre321/oneof/blob/master/README.md Demonstrates returning different OneOf types to represent success or various error conditions from a method. The consumer must use 'Match' to handle all possible outcomes. ```csharp public OneOf CreateUser(string username) { if (!IsValid(username)) return new InvalidName(); var user = _repo.FindByUsername(username); if(user != null) return new NameTaken(); var user = new User(username); _repo.Save(user); return user; } ``` ```csharp [HttpPost] public IActionResult Register(string username) { OneOf createUserResult = CreateUser(username); return createUserResult.Match( user => new RedirectResult("/dashboard"), invalidName => { ModelState.AddModelError(nameof(username), $"Sorry, that is not a valid username."); return View("Register"); }, nameTaken => { ModelState.AddModelError(nameof(username), "Sorry, that name is already in use."); return View("Register"); } ); } ``` -------------------------------- ### Match a OneOf to get a Color Source: https://github.com/mcintyre321/oneof/blob/master/README.md Use the Match method to convert a OneOf to a single return type. Ensure all possible types within the OneOf are handled. ```csharp OneOf backgroundColor = ...; Color c = backgroundColor.Match( str => CssHelper.GetColorFromString(str), name => new Color(name), col => col ); _window.BackgroundColor = c; ``` -------------------------------- ### Chaining Async Operations with OneOf Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/integration-guide.md Illustrates how to compose multiple asynchronous operations, each returning a OneOf type, ensuring errors are propagated correctly. ```csharp public class OrderProcessor { public async Task> ProcessOrderAsync(Order order) { var validation = await ValidateOrderAsync(order); if (validation.TryPickT1(out var validError, out var _)) { return validError; } var payment = await ProcessPaymentAsync(order); if (payment.TryPickT1(out var paymentError, out var _)) { return paymentError; } var saved = await SaveOrderAsync(order); if (saved.TryPickT1(out var saveError, out var _)) { return saveError; } return order; } } ``` -------------------------------- ### Get Hash Code for OneOf Instance Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/api-reference-oneof-struct.md Returns a hash code based on the contained value's hash code and the type index. This is useful for using OneOf instances in collections that require hashing. ```csharp public override int GetHashCode() { // ... implementation details ... } ``` -------------------------------- ### Use TrueFalseOrNull with Nullable Bool Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/types.md Demonstrates using the TrueFalseOrNull discriminated union with implicit conversion from a nullable boolean value and matching the result. ```csharp TrueFalseOrNull value = (bool?)null; // Converts to Null int result = value.Match( t => 1, f => 0, n => -1 ); // result = -1 ``` -------------------------------- ### Polymorphic Storage with OneOf Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/api-reference-iooneof.md Demonstrates storing different OneOf instances in a collection of IOneOf. Ensure OneOf types are correctly cast or implicitly converted when added. ```csharp List results = new(); results.Add((OneOf)"hello"); results.Add((OneOf)42); results.Add((OneOf)true); foreach (var result in results) { Console.WriteLine($"Type index: {result.Index}, Value: {result.Value}"); } // Output: // Type index: 0, Value: hello // Type index: 1, Value: 42 // Type index: 0, Value: True ``` -------------------------------- ### Equality and Hashing Methods Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/api-reference-oneof-base.md Provides methods for comparing OneOfBase instances for equality and obtaining their hash codes. Two instances are considered equal if they contain the same type at the same index and their contained values are equal. ```APIDOC ## Equality and Hashing ### Equals #### Description Two OneOfBase instances are equal if they hold the same type at the same index and the contained values are equal. #### Method Signature ```csharp public override bool Equals(object obj) ``` #### Example ```csharp public class Value : OneOfBase { Value(OneOf _) : base(_) { } public static implicit operator Value(int i) => new Value(i); public static implicit operator Value(string s) => new Value(s); } var a = (Value)42; var b = (Value)42; bool isEqual = a.Equals(b); // true ``` ### GetHashCode #### Description Returns a hash code consistent with equality. #### Method Signature ```csharp public override int GetHashCode() ``` ### ToString #### Description Returns a string representation in the format `TypeName: value`. #### Method Signature ```csharp public override string ToString() ``` ``` -------------------------------- ### Self-Documenting Method Signatures with OneOf Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/package-overview.md The return type of a method using OneOf clearly communicates all possible outcomes, serving as documentation. This enhances understanding of the method's behavior without needing to read its implementation. ```csharp public OneOf Login(string username, string password) // Method signature tells you exactly what can happen ``` -------------------------------- ### Constructor Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/api-reference-oneof-base.md The protected constructor requires derived classes to initialize OneOfBase with an existing OneOf instance. ```APIDOC ## Protected Constructor ```csharp protected OneOfBase(OneOf input) ``` ### Parameters - **input** (OneOf) - Description: An existing OneOf instance to wrap. ### Example ```csharp public class Result : OneOfBase { Result(OneOf _) : base(_) { } } ``` ``` -------------------------------- ### No Pattern Matching via IOneOf Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/api-reference-iooneof.md Highlights that pattern matching methods like 'Match' and 'Switch' are not available directly on the IOneOf interface. These methods require the concrete OneOf type. ```csharp IOneOf result = (OneOf)42; // These methods are not available on IOneOf // result.Match(i => i, s => s); // Error: not defined on IOneOf // result.Switch(i => { }, s => { }); // Error: not defined on IOneOf ``` -------------------------------- ### Workaround: Cast Back to Concrete Type with IOneOf Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/api-reference-iooneof.md Demonstrates the workaround for limitations with IOneOf by casting back to the concrete OneOf type. This allows for pattern matching using 'Match'. ```csharp IOneOf result = (OneOf)42; // Cast back to concrete type if (result is OneOf concrete) { string output = concrete.Match( i => $"Int: {i}", s => $"String: {s}" ); } ``` -------------------------------- ### Execute Actions for Each Type with Switch Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/errors.md Similar to Match, the Switch method allows executing specific actions for each type in a OneOf object. It ensures no exceptions occur if all handlers are provided. ```csharp OneOf value = GetResult(); value.Switch( str => Console.WriteLine($"String: {str}"), num => Console.WriteLine($"Number: {num}") ); // No exceptions possible if all handlers are provided ``` -------------------------------- ### Equality and Comparison Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/api-reference-summary.md Details the implementation of equality and comparison for all OneOf types, ensuring structural equality based on type index and wrapped values. ```APIDOC ## Equality and Comparison ### Description All OneOf types provide implementations for structural equality and hashing, ensuring consistent behavior. ### Methods - **bool Equals(object obj)**: Compares two OneOf instances for structural equality. - **int GetHashCode()**: Returns a hash code consistent with the equality implementation. - **string ToString()**: Formats the OneOf instance as `TypeName: value`. ### Equality Criteria 1. The type index must be the same for both instances. 2. The wrapped values within the instances must be equal. ``` -------------------------------- ### OneOf and Beyond Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/api-reference-summary.md Generalizes the OneOf pattern for up to 9 types in the core package and up to 32 types in the extended package, following a consistent structure for properties, methods, and factories. ```APIDOC ## OneOf and Beyond ### Description Generalizes the OneOf pattern for up to 9 types in the core package and up to 32 types in the extended package, following a consistent structure for properties, methods, and factories. ### Common Pattern for `OneOf` - **Properties**: `IsT{n}` (bool), `AsT{n}` (T{n}) - **Methods**: `Match(Func..., Func) → TResult`, `Switch(Action..., Action) → void`, `MapT{n}(Func) → OneOf<...>` - **Static Factories**: `FromT{n}(T{n} input) → OneOf` - **Conditional Extraction**: `TryPickT{n}(out T{n} value, out OneOf<...> remainder) → bool` - **Operators**: `implicit OneOf(T{n})` ### Package Variations - **Core Package**: Supports up to `OneOf` (9 types). - **Extended Package**: Supports up to `OneOf` (32 types). ``` -------------------------------- ### IOneOf Interface Implementation Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/api-reference-oneof-struct.md All OneOf instances implement the IOneOf interface, allowing polymorphic treatment. ```APIDOC ## IOneOf Interface ### Description All OneOf instances implement the `IOneOf` interface, which provides access to the contained value and its type index. ### Interface Definition ```csharp public interface IOneOf { object Value { get; } int Index { get; } } ``` ### Remarks This interface allows treating OneOf instances polymorphically. ``` -------------------------------- ### Option with Custom Members Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/api-reference-oneof-base.md An Option type implementation using OneOfBase, demonstrating how to add custom members like `IsSome` and `TryGetValue` for domain-specific functionality. This is useful for representing the presence or absence of a value. ```csharp public class Option : OneOfBase { Option(OneOf _) : base(_) { } public static implicit operator Option(T value) => new Option(value); public static implicit operator Option(None _) => new Option(new OneOf(new None())); public bool IsSome => IsT0; public bool TryGetValue(out T value) { value = IsT0 ? AsT0 : default; return IsSome; } } ``` -------------------------------- ### OneOfBase Constructor Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/api-reference-summary.md Initializes OneOfBase from an existing OneOf instance. This constructor is protected and intended for internal use or derived classes. ```APIDOC ## protected OneOfBase(OneOf input) ### Description Initializes a new instance of the OneOfBase class from a OneOf instance. ### Parameters - **input** (OneOf) - Required - The OneOf instance to initialize from. ``` -------------------------------- ### Generic Processing with OneOf Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/api-reference-iooneof.md Illustrates processing OneOf instances generically using the IOneOf interface. The Logger class can handle any IOneOf result, accessing its index and value. ```csharp class Logger { public void LogResult(IOneOf result) { Console.WriteLine($"Result[{result.Index}] = {result.Value}"); } } var logger = new Logger(); logger.LogResult((OneOf)42); logger.LogResult((OneOf)"test"); ``` -------------------------------- ### Switch with OneOfBase Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/api-reference-oneof-base.md Use Switch to execute an action based on the contained type without returning a value. All possible types must have a corresponding action. ```csharp public void Switch(Action f0, Action f1, Action f2) ``` ```csharp public class Logger : OneOfBase { Logger(OneOf _) : base(_) { } public void Log() { Switch( info => Console.WriteLine($"INFO: {info.Message}"), warn => Console.WriteLine($"WARN: {warn.Message}"), err => Console.WriteLine($"ERROR: {err.Message}") ); } } ``` -------------------------------- ### Ensure All Handlers for Match Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/errors.md When using the Match method, always provide non-null handlers for all possible types to prevent runtime errors. Incomplete handlers can lead to exceptions. ```csharp // GOOD - all handlers provided string result = value.Match( str => $"String: {str}", num => $"Number: {num}" ); ``` -------------------------------- ### Basic Result Implementation Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/api-reference-oneof-base.md A basic implementation of a Result type using OneOfBase, providing implicit operators for constructing success or error states. Use this pattern when you need a type that can hold either a success value or an error value. ```csharp public class Result : OneOfBase { Result(OneOf _) : base(_) { } public static implicit operator Result(TSuccess success) => new Result(success); public static implicit operator Result(TError error) => new Result(error); } // Usage Result result = 42; string output = result.Match( success => $"Got: {success}", error => $"Error: {error}" ); ``` -------------------------------- ### TryPickTn Methods Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/api-reference-oneof-base.md These methods attempt to extract a value of a specific type (Tn) from the OneOfBase instance. They populate an output parameter with the extracted value and another with the remaining types in the OneOfBase. A boolean is returned indicating success or failure. ```APIDOC ## TryPickTn Methods ### Description Attempts to extract the value of type Tn, populating both `value` and a `remainder` OneOf. This is useful for conditionally handling different types within the OneOfBase. ### Method Signature ```csharp public bool TryPickT0(out T0 value, out OneOf remainder) ``` ### Parameters #### Output Parameters - **value** (out Tn) - The extracted value if present, else default. - **remainder** (out OneOf<...>) - OneOf containing all types except Tn. ### Returns - **bool** - `true` if the OneOfBase held Tn, `false` otherwise. ### Example ```csharp public class ApiResult : OneOfBase { ApiResult(OneOf _) : base(_) { } public IActionResult ToHttpResponse() { if (TryPickT1(out var notFound, out var remaining)) { return NotFound(); } if (remaining.TryPickT1(out var error, out var success)) { return StatusCode(500); } return Ok(success.AsT0); } } ``` ``` -------------------------------- ### Composable Validation Pipeline Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/advanced-patterns.md Create a flexible validation pipeline where each step can return either a success indicator or a specific validation error. This allows for building complex validation logic from smaller, reusable parts. ```csharp public class ValidationPipeline { private List>> _validators = new(); public ValidationPipeline Add( string fieldName, Func predicate, string errorMessage) { _validators.Add(item => predicate(item) ? true : (ValidationError)new ValidationError(fieldName, errorMessage) ); return this; } public OneOf> Validate(T item) { var errors = new List(); foreach (var validator in _validators) { var result = validator(item); result.Switch( success => { }, error => errors.Add(error) ); } return errors.Count == 0 ? item : errors; } } // Usage var pipeline = new ValidationPipeline() .Add("Name", u => !string.IsNullOrEmpty(u.Name), "Name is required") .Add("Email", u => u.Email.Contains("@"), "Invalid email format") .Add("Age", u => u.Age >= 18, "Must be 18 or older"); var result = pipeline.Validate(user); result.Switch( validUser => SaveUser(validUser), errors => DisplayErrors(errors) ); ``` -------------------------------- ### Key Properties Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/api-reference-oneof-base.md Provides access to the index of the current type and the wrapped value. ```APIDOC ## Index Property ```csharp public int Index { get; } ``` Returns the zero-based index of which type is currently held (inherited from wrapped OneOf). ``` ```APIDOC ## Value Property ```csharp public object Value { get; } ``` Returns the wrapped value as an `object` (inherited from wrapped OneOf). ``` -------------------------------- ### OneOf Common Methods Signature Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/api-reference-summary.md This snippet outlines the common methods available across all OneOf arities, including properties for accessing the wrapped value and its index, type-specific accessors, and methods for switching, matching, mapping, and attempting to pick values. ```csharp public object Value { get; } // Wrapped value as object public int Index { get; } // Type index public bool IsT0 { get; } // For each Tn public T0 AsT0 { get; } // For each Tn public void Switch(Action a0, Action a1, ... Action an) public TResult Match(Func f0, ..., Func fn) public OneOf MapT0(Func mapFunc) // For each Tn public bool TryPickT0(out T0 value, out OneOf remainder) // For each Tn public static OneOf FromT0(T0 input) // For each Tn public static implicit operator OneOf(T0 t) // For each Tn public override bool Equals(object obj) public override int GetHashCode() public override string ToString() ``` -------------------------------- ### Accessing Value with AsT0 and Type Checking Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/api-reference-oneof-base.md Shows how to use the AsT0 property to access the value of the first type and combine it with type-checking properties like IsT0. This pattern is common for handling different states of a discriminated union. ```csharp public class MyResult : OneOfBase { MyResult(OneOf _) : base(_) { } public string GetStatus() => IsT0 ? $ ``` -------------------------------- ### OneOf Test Extensions for Assertions Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/integration-guide.md Create reusable test extensions for asserting OneOf results in unit tests. Includes methods for asserting success, error, and retrieving values. ```csharp public static class OneOfTestExtensions { public static void AssertSuccess( this OneOf result) { Assert.IsTrue(result.IsT0, $"Expected success but got error: {result.AsT1}"); } public static void AssertError( this OneOf result, Action assert) { Assert.IsTrue(result.IsT1, "Expected error but got success"); assert(result.AsT1); } public static TSuccess GetSuccess( this OneOf result) { Assert.IsTrue(result.IsT0); return result.AsT0; } } // Usage in tests [Test] public void CreateUser_WithValidInput_ReturnsUser() { var result = _userService.CreateUser(validRequest); result.AssertSuccess(); var user = result.GetSuccess(); Assert.AreEqual("John", user.Name); } [Test] public void CreateUser_WithDuplicateEmail_ReturnsError() { var result = _userService.CreateUser(duplicateRequest); result.AssertError(error => { Assert.AreEqual("DuplicateEmail", error.Code); }); } ``` -------------------------------- ### Factory Methods: FromT0, FromT1, FromT2 Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/api-reference-oneof-struct.md Provides static factory methods to explicitly construct a OneOf from a specific type. These are alternatives to implicit operators for clarity. ```APIDOC ## Factory Methods: FromT0, FromT1, FromT2 ### Description Provides static factory methods to explicitly construct a OneOf from a specific type. These are alternatives to implicit operators for clarity. ### Methods - `public static OneOf FromT0(T0 input)` - `public static OneOf FromT1(T1 input)` - `public static OneOf FromT2(T2 input)` ### Example ```csharp OneOf value = OneOf.FromT1(42); // Equivalent to: OneOf value = 42; ``` ``` -------------------------------- ### OneOf as a Method Parameter Source: https://github.com/mcintyre321/oneof/blob/master/README.md Shows how OneOf can be used as a method parameter, allowing callers to pass values of different specified types without needing multiple overloads. ```csharp public void SetBackground(OneOf backgroundColor) { ... } ``` -------------------------------- ### Match with OneOfBase Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/api-reference-oneof-base.md Use Match to execute a function based on the contained type and return a result. Ensure all possible types are handled. ```csharp public TResult Match(Func f0, Func f1, Func f2) { // ... implementation details ... } ``` ```csharp public class Response : OneOfBase { Response(OneOf _) : base(_) { } public IActionResult GetHttpResponse() { return Match( data => Ok(data), notFound => NotFound(), error => StatusCode(500, error) ); } } ``` -------------------------------- ### Select Union Type Based on Configuration Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/integration-guide.md Use OneOf to return different service implementations based on configuration values. This is useful for dependency injection scenarios where the concrete type is determined at runtime. ```csharp public class ConfiguredServiceFactory { private readonly IConfiguration _config; public ConfiguredServiceFactory(IConfiguration config) => _config = config; public OneOf CreateDataService() { var providerType = _config.GetValue("DataProvider:Type"); return providerType?.ToLower() switch { "sql" => CreateSqlService(), "mongo" => CreateMongoService(), "http" => CreateHttpService(), null => new ConfigurationError { Message = "DataProvider:Type not configured" }, _ => new ConfigurationError { Message = $"Unknown provider: {providerType}" } }; } } ``` -------------------------------- ### Chaining Operations with TryPickTn Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/common-patterns.md Use TryPickTn to sequentially handle different outcomes from an OneOf result. This is useful for processing a series of potential results where each step depends on the success of the previous one. ```csharp OneOf result = ProcessRequest(); if (result.TryPickT1(out var validationError, out var remaining1)) { return BadRequest(validationError); } if (remaining1.TryPickT1(out var authError, out var remaining2)) { return Unauthorized(authError); } if (remaining2.TryPickT1(out var serverError, out var success)) { return StatusCode(500, serverError); } return Ok(success.AsT0); ``` -------------------------------- ### Async Operations with OneOf Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/integration-guide.md Demonstrates fetching data asynchronously and returning a OneOf type to represent success, not found, or server error states. ```csharp public class DataService { public async Task> GetDataAsync(int id) { try { var data = await _httpClient.GetAsync($"/api/data/{id}"); return data.IsSuccessStatusCode ? await data.Content.ReadAsAsync() : (OneOf)new NotFound(); } catch (HttpRequestException ex) { return new ServerError { Message = ex.Message }; } } } ``` -------------------------------- ### Option/Maybe Pattern with OneOf Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/README.md Represents a value that may or may not be present. Use this pattern when a value can either exist or be absent. ```csharp OneOf // Value or absence ``` -------------------------------- ### Switch Method Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/api-reference-oneof-base.md Executes exactly one handler based on which type is held. This method does not return a value and is used for side effects or actions. ```APIDOC ## Switch (Execute Without Return) ### Description Executes exactly one handler based on which type is held. No return value. This method is used for performing actions or side effects based on the contained type. ### Method Signature ```csharp public void Switch(Action f0, Action f1, Action f2) ``` ### Parameters #### Handler Actions - **f0** (`Action`) - Required - Handler for T0 value - **f1** (`Action`) - Required - Handler for T1 value - **f2** (`Action`) - Required - Handler for T2 value ### Example ```csharp public class Logger : OneOfBase { Logger(OneOf _) : base(_) { } public void Log() { Switch( info => Console.WriteLine($"INFO: {info.Message}"), warn => Console.WriteLine($"WARN: {warn.Message}"), err => Console.WriteLine($"ERROR: {err.Message}") ); } } ``` ``` -------------------------------- ### Feature Flags with OneOf Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/integration-guide.md Integrate OneOf with feature flag management to conditionally return different behaviors. This allows for A/B testing or phased rollouts. ```csharp public class FeatureFlagService { private readonly IFeatureManager _featureManager; public async Task> GetBehavior(string feature) { var enabled = await _featureManager.IsEnabledAsync(feature); return enabled ? new NewBehavior() : (OneOf)new LegacyBehavior(); } } ``` -------------------------------- ### OneOf Factory Methods Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/api-reference-oneof-struct.md Use factory methods like FromT0, FromT1, etc., as an alternative to implicit operators for explicitly constructing a OneOf from a specific type. ```csharp public static OneOf FromT0(T0 input) => input; public static OneOf FromT1(T1 input) => input; public static OneOf FromT2(T2 input) => input; ``` ```csharp OneOf value = OneOf.FromT1(42); // Equivalent to: OneOf value = 42; ``` -------------------------------- ### Helper Method for Success Case Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/api-reference-summary.md Implement a helper method to conditionally execute a function when the OneOf is in the success state (T0). ```csharp public TResult IfSuccess(Func func) => IsSuccess ? func(AsT0) : default; ``` -------------------------------- ### MVC Controller Actions with OneOf Source: https://github.com/mcintyre321/oneof/blob/master/_autodocs/integration-guide.md Use OneOf to return different HTTP responses from controller actions. The Match method handles each possible outcome. ```csharp using Microsoft.AspNetCore.Mvc; using OneOf; [ApiController] [Route("api/[controller]")] public class UsersController : ControllerBase { [HttpGet("{id}")] public IActionResult GetUser(int id) { OneOf result = GetUserIfAuthorized(id); return result.Match( user => Ok(user), notFound => NotFound(), unauthorized => Unauthorized() ); } [HttpPost] public IActionResult CreateUser(CreateUserRequest request) { OneOf result = CreateUserInternal(request); return result.Match( user => CreatedAtAction(nameof(GetUser), new { id = user.Id }, user), validation => BadRequest(validation), duplicate => Conflict(new { error = "User already exists" }) ); } } ```