├── LICENSE ├── img └── logo.png ├── .gitignore ├── tests └── Ultimately.Tests │ ├── Properties │ └── AssemblyInfo.cs │ ├── Ultimately.Tests.csproj │ ├── EitherTests.cs │ └── CollectionTests.cs ├── src ├── Ultimately.csproj ├── Reasons │ ├── ExceptionalError.cs │ ├── Reason.cs │ ├── ReasonStringBuilder.cs │ ├── Success.cs │ └── Error.cs ├── Option_Unit_Lazy.cs ├── Utilities │ ├── MissingReasons.cs │ └── TryParse.cs ├── Option_Unit.cs ├── OptionExtensions.cs ├── Optional.cs ├── Collections │ └── OptionCollectionExtensions.cs └── Option_Either.cs ├── Ultimately.sln └── README.md /LICENSE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silkfire/Ultimately/HEAD/LICENSE -------------------------------------------------------------------------------- /img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silkfire/Ultimately/HEAD/img/logo.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.suo 2 | *.user 3 | _ReSharper.* 4 | .vs 5 | bin 6 | obj 7 | packages 8 | -------------------------------------------------------------------------------- /tests/Ultimately.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | [assembly: AssemblyTitle("Optional.Tests")] 5 | [assembly: AssemblyDescription("")] 6 | [assembly: AssemblyConfiguration("")] 7 | [assembly: AssemblyCompany("")] 8 | [assembly: AssemblyProduct("Ultimately.Tests")] 9 | [assembly: AssemblyCopyright("Copyright © 2019")] 10 | [assembly: AssemblyTrademark("")] 11 | [assembly: AssemblyCulture("")] 12 | 13 | [assembly: AssemblyVersion("1.0.0.0")] 14 | [assembly: AssemblyFileVersion("1.0.0.0")] 15 | 16 | [assembly: ComVisible(false)] 17 | -------------------------------------------------------------------------------- /src/Ultimately.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net10.0 5 | Gabriel Bider, Nils Lück, Michael Altmann 6 | option optional some none maybe either null monad monadic result results resultful fluentresults functional pipeline pipelines 7 | https://github.com/silkfire/Ultimately 8 | A robust option type for C#. 9 | 4.0.0 10 | 4.0.0.0 11 | 4.0.0 Target .NET 10 12 | 13 | 14 | -------------------------------------------------------------------------------- /tests/Ultimately.Tests/Ultimately.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net10.0 5 | true 6 | 7 | false 8 | Gabriel Bider, Nils Lück 9 | 1591 10 | 11 | 12 | 13 | 14 | 15 | all 16 | runtime; build; native; contentfiles; analyzers; buildtransitive 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/Reasons/ExceptionalError.cs: -------------------------------------------------------------------------------- 1 | namespace Ultimately.Reasons; 2 | 3 | using System; 4 | 5 | /// 6 | /// Represents an error that is associated with a specific exception instance. 7 | /// 8 | public sealed class ExceptionalError : Error 9 | { 10 | /// 11 | /// Gets the exception associated with this error. 12 | /// 13 | public Exception Exception { get; } 14 | 15 | private ExceptionalError(string message, Exception exception) : base(message) 16 | { 17 | Exception = exception; 18 | } 19 | 20 | /// 21 | /// Creates a new instance that wraps the specified exception. 22 | /// 23 | /// The exception to wrap. 24 | public static ExceptionalError Create(Exception exception) 25 | { 26 | return new ExceptionalError(exception.Message, exception); 27 | } 28 | 29 | /// 30 | protected internal override ReasonStringBuilder GetReasonStringBuilder() 31 | { 32 | return new ReasonStringBuilder().WithInfoNoQuotes("", $"{Exception.GetType().Name}: '{Message}'"); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Reasons/Reason.cs: -------------------------------------------------------------------------------- 1 | namespace Ultimately.Reasons; 2 | 3 | using System.Collections.Generic; 4 | 5 | // https://github.com/altmann/FluentResults 6 | /// 7 | /// Base class for representing reasons for success or failure. 8 | /// 9 | public abstract class Reason 10 | { 11 | /// 12 | /// Gets the message associated with the current object. 13 | /// 14 | public string Message { get; protected set; } 15 | 16 | /// 17 | /// Gets the collection of key-value pairs that store additional metadata associated with the object. 18 | /// 19 | public Dictionary Metadata { get; protected set; } = []; 20 | 21 | /// 22 | /// Gets a for this reason. 23 | /// 24 | protected internal virtual ReasonStringBuilder GetReasonStringBuilder() 25 | { 26 | return new ReasonStringBuilder().WithReasonType(GetType()) 27 | .WithInfo("", Message) 28 | .WithInfo(nameof(Metadata), string.Join("; ", Metadata)); 29 | } 30 | 31 | /// 32 | /// Returns a string representation of the reason. 33 | /// 34 | public override string ToString() 35 | { 36 | return GetReasonStringBuilder().Build(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Ultimately.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.28803.352 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ultimately", "src\Ultimately.csproj", "{BDAE4370-B7A7-41B4-BCF9-8D201C811914}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ultimately.Tests", "tests\Ultimately.Tests\Ultimately.Tests.csproj", "{12606143-9C9B-4A92-81D5-CEA814AC64AC}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {BDAE4370-B7A7-41B4-BCF9-8D201C811914}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {BDAE4370-B7A7-41B4-BCF9-8D201C811914}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {BDAE4370-B7A7-41B4-BCF9-8D201C811914}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {BDAE4370-B7A7-41B4-BCF9-8D201C811914}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {12606143-9C9B-4A92-81D5-CEA814AC64AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {12606143-9C9B-4A92-81D5-CEA814AC64AC}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {12606143-9C9B-4A92-81D5-CEA814AC64AC}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {12606143-9C9B-4A92-81D5-CEA814AC64AC}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {E57277B7-1604-4E19-94FB-95F188F94526} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /src/Option_Unit_Lazy.cs: -------------------------------------------------------------------------------- 1 | namespace Ultimately; 2 | 3 | using Reasons; 4 | 5 | using System; 6 | using System.Threading.Tasks; 7 | 8 | /// 9 | /// Wraps a predicate that returns a successful or an unsuccessful outcome. 10 | /// 11 | public readonly struct LazyOption 12 | { 13 | internal Func OutcomeDelegate { get; } 14 | 15 | internal Success Success { get; } 16 | 17 | internal Error Error { get; } 18 | 19 | internal LazyOption(Func outcomeDelegate, Success success, Error error) 20 | { 21 | OutcomeDelegate = outcomeDelegate; 22 | Success = success; 23 | Error = error; 24 | } 25 | 26 | /// 27 | /// Resolves the outcome delegate of the optional, returning a regular optional whose outcome depends on the result of the delegate. 28 | /// 29 | /// 30 | public Option Resolve() 31 | { 32 | return OutcomeDelegate() ? Optional.Some(Success) : Optional.None(Error); 33 | } 34 | } 35 | 36 | /// 37 | /// Wraps a predicate task that returns a successful or an unsuccessful outcome. 38 | /// 39 | public readonly struct LazyOptionAsync 40 | { 41 | internal Func> OutcomeDelegate { get; } 42 | 43 | internal Success Success { get; } 44 | 45 | internal Error Error { get; } 46 | 47 | internal LazyOptionAsync(Func> outcomeDelegate, Success success, Error error) 48 | { 49 | OutcomeDelegate = outcomeDelegate; 50 | Success = success; 51 | Error = error; 52 | } 53 | 54 | /// 55 | /// Resolves the outcome delegate of the optional, returning a regular optional whose outcome depends on the result of the delegate. 56 | /// 57 | /// 58 | public async Task