{"id":28209325,"url":"https://github.com/altasoft/choice","last_synced_at":"2026-04-05T17:36:57.156Z","repository":{"id":291263307,"uuid":"977109115","full_name":"altasoft/Choice","owner":"altasoft","description":"AltaSoft.Choice provides the [Choice] attribute to define discriminated union types in C#, enabling source-generated, type-safe choice models.","archived":false,"fork":false,"pushed_at":"2025-05-16T07:18:55.000Z","size":2808,"stargazers_count":11,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-07-05T19:36:16.016Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/altasoft.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-05-03T12:52:26.000Z","updated_at":"2025-05-22T20:22:49.000Z","dependencies_parsed_at":"2025-05-03T13:52:54.145Z","dependency_job_id":"bf6aa66f-a998-4680-abce-6dbd62d7807a","html_url":"https://github.com/altasoft/Choice","commit_stats":null,"previous_names":["altasoft/choice"],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/altasoft/Choice","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/altasoft%2FChoice","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/altasoft%2FChoice/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/altasoft%2FChoice/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/altasoft%2FChoice/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/altasoft","download_url":"https://codeload.github.com/altasoft/Choice/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/altasoft%2FChoice/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264441549,"owners_count":23608906,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-05-17T15:14:10.423Z","updated_at":"2026-04-05T17:36:52.133Z","avatar_url":"https://github.com/altasoft.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"﻿# AltaSoft.Choice\n\n[![NuGet - AltaSoft.Choice](https://img.shields.io/nuget/v/AltaSoft.Choice?label=AltaSoft.Choice)](https://www.nuget.org/packages/AltaSoft.Choice)\n[![NuGet - AltaSoft.Choice.Generator](https://img.shields.io/nuget/v/AltaSoft.Choice.Generator?label=AltaSoft.Choice.Generator)](https://www.nuget.org/packages/AltaSoft.Choice.Generator)\n[![Dot NET 8+](https://img.shields.io/static/v1?label=DOTNET\u0026message=8%2B\u0026color=0c3c60\u0026style=for-the-badge)](https://dotnet.microsoft.com)\n\n**AltaSoft.ChoiceGenerator** is a lightweight C# source generator that allows you to define *choice types* (discriminated unions) with minimal syntax.\n\n---\n\n## ✨ Features\n\n- Simple `[Choice]` attribute for defining alternatives\n- Generates type-safe properties\n- Supports XML and System.Text.Json serialization\n- Includes `CreateAsXxx`, `Match`, and `Switch` methods\n- Auto-generates enum for valid choice types\n- Implicit conversion operators for easy usage\n- Generates XmlSerializer\n\n---\n\n## 🛠️ Installation\n\nAdd the following NuGet packages to your project:\n\n```xml\n\u003cItemGroup\u003e\n  \u003cPackageReference Include=\"AltaSoft.Choice\" Version=\"x.x.x\" /\u003e\n  \u003cPackageReference Include=\"AltaSoft.Choice.Generator\" Version=\"x.x.x\" PrivateAssets=\"all\" /\u003e\n\u003c/ItemGroup\u003e\n```\n\n---\n\n## ✅ Define Your Choice Type\n\nMark your class with `[Choice]` and define **partial nullable properties** :\n\n```csharp\nusing AltaSoft.Choice;\nnamespace AltaSoft.ChoiceGenerator.Tests;\n\n[Choice]\npublic sealed partial class Authorisation1Choice\n{\n    /// \u003csummary\u003e\n    /// \u003cpara\u003eSpecifies the authorisation, in a coded form.\u003c/para\u003e\n    /// \u003c/summary\u003e\n    [XmlTag(\"Cd\")]\n    [JsonPropertyName(\"cd\")]\n    public partial Authorisation1Code? Code { get; set; }\n\n    /// \u003csummary\u003e\n    /// \u003cpara\u003eSpecifies the authorisation, in a free text form.\u003c/para\u003e\n    /// \u003c/summary\u003e\n    [XmlTag(\"Prtry\")]\n    public partial Proprietary? Proprietary { get; set; }\n}\n```\n\n---\n\n## ⚙️ Generated Code\n\nBelow is the generated code for the example above:\n\n```csharp\n//------------------------------------------------------------------------------\n// \u003cauto-generated\u003e\n//     This code was generated by 'AltaSoft Choice.Generator'.\n//     Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.\n// \u003c/auto-generated\u003e\n//------------------------------------------------------------------------------\n\n#nullable enable\n\nusing AltaSoft.ChoiceGenerator.Tests;\nusing AltaSoft.Choice;\nusing System;\nusing System.ComponentModel;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Globalization;\nusing System.Text.Json;\nusing System.Text.Json.Serialization;\nusing System.Xml;\nusing System.Xml.Serialization;\nusing System.Xml.Schema;\n\nnamespace AltaSoft.ChoiceGenerator.Tests;\n\n#pragma warning disable CS8774 // Member must have a non-null value when exiting.\n#pragma warning disable CS0628 // New protected member declared in sealed type\n\npublic sealed partial record Authorisation1Choice\n{\n    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]\n    public Authorisation1Choice()\n    {\n    }\n\n    /// \u003csummary\u003e\n    /// \u003cpara\u003eChoice enum \u003c/para\u003e\n    /// \u003c/summary\u003e\n    [JsonIgnore]\n    [XmlIgnore]\n    public ChoiceOf ChoiceType { get; private set; }\n\n    private AltaSoft.ChoiceGenerator.Tests.Authorisation1Code? _code;\n\n    /// \u003csummary\u003e\n    /// Specifies the authorisation, in a coded form.\n    /// \u003c/summary\u003e\n    [DisallowNull]\n    [XmlElement(\"Cd\")]\n    public partial AltaSoft.ChoiceGenerator.Tests.Authorisation1Code? Code\n    {\n        get =\u003e _code;\n        set\n        {\n            _code = value ?? throw new InvalidOperationException(\"Choice value cannot be null\");\n            _proprietary = null;\n            ChoiceType = ChoiceOf.Code;\n        }\n    }\n\n    private Proprietary? _proprietary;\n\n    /// \u003csummary\u003e\n    /// Specifies the authorisation, in a free text form.\n    /// \u003c/summary\u003e\n    [DisallowNull]\n    [XmlElement(\"Prtry\")]\n    public partial Proprietary? Proprietary\n    {\n        get =\u003e _proprietary;\n        set\n        {\n            _proprietary = value ?? throw new InvalidOperationException(\"Choice value cannot be null\");\n            _code = null;\n            ChoiceType = ChoiceOf.Proprietary;\n        }\n    }\n\n\n    /// \u003csummary\u003e\n    /// Creates a new \u003csee cref=\"AltaSoft.ChoiceGenerator.Tests.Authorisation1Choice\"/\u003e instance and sets its value using the specified \u003csee cref=\"AltaSoft.ChoiceGenerator.Tests.Authorisation1Code\"/\u003e.\n    /// \u003c/summary\u003e\n    /// \u003cparam name=\"value\"\u003eThe value to assign to the created choice instance.\u003c/param\u003e\n    public static AltaSoft.ChoiceGenerator.Tests.Authorisation1Choice CreateAsCode(AltaSoft.ChoiceGenerator.Tests.Authorisation1Code value) =\u003e new () { Code = value };\n\n    /// \u003csummary\u003e\n    /// Creates a new \u003csee cref=\"AltaSoft.ChoiceGenerator.Tests.Authorisation1Choice\"/\u003e instance and sets its value using the specified \u003csee cref=\"Proprietary\"/\u003e.\n    /// \u003c/summary\u003e\n    /// \u003cparam name=\"value\"\u003eThe value to assign to the created choice instance.\u003c/param\u003e\n    public static AltaSoft.ChoiceGenerator.Tests.Authorisation1Choice CreateAsProprietary(Proprietary value) =\u003e new () { Proprietary = value };\n\n    /// \u003csummary\u003e\n    /// \u003cpara\u003eApplies the appropriate function based on the current choice type\u003c/para\u003e\n    /// \u003c/summary\u003e\n    /// \u003ctypeparam name=\"TResult\"\u003eThe return type of the provided match functions\u003c/typeparam\u003e\n    /// \u003cparam name=\"matchCode\"\u003eFunction to invoke if the choice is a \u003csee cref=\"ChoiceOf.Code\"/\u003e value\u003c/param\u003e\n    /// \u003cparam name=\"matchProprietary\"\u003eFunction to invoke if the choice is a \u003csee cref=\"ChoiceOf.Proprietary\"/\u003e value\u003c/param\u003e\n    public TResult Match\u003cTResult\u003e(\n    \tFunc\u003cAltaSoft.ChoiceGenerator.Tests.Authorisation1Code, TResult\u003e matchCode, \n    \tFunc\u003cProprietary, TResult\u003e matchProprietary)\n    {\n        return ChoiceType switch\n        {\n            ChoiceOf.Code =\u003e matchCode(Code!.Value),\n            ChoiceOf.Proprietary =\u003e matchProprietary(Proprietary!),\n            _ =\u003e throw new InvalidOperationException($\"Invalid ChoiceType. '{ChoiceType}'\")\n        };\n    }\n\n    /// \u003csummary\u003e\n    /// \u003cpara\u003eApplies the appropriate Action based on the current choice type\u003c/para\u003e\n    /// \u003c/summary\u003e\n    /// \u003cparam name=\"matchCode\"\u003eAction to invoke if the choice is a \u003csee cref=\"ChoiceOf.Code\"/\u003e value\u003c/param\u003e\n    /// \u003cparam name=\"matchProprietary\"\u003eAction to invoke if the choice is a \u003csee cref=\"ChoiceOf.Proprietary\"/\u003e value\u003c/param\u003e\n    public void Switch(\n    \tAction\u003cAltaSoft.ChoiceGenerator.Tests.Authorisation1Code\u003e matchCode, \n    \tAction\u003cProprietary\u003e matchProprietary)\n    {\n        switch (ChoiceType)\n        {\n            case ChoiceOf.Code:\n                matchCode(Code!.Value);\n                return;\n\n            case ChoiceOf.Proprietary:\n                matchProprietary(Proprietary!);\n                return;\n\n            default:\n            throw new XmlException($\"Invalid ChoiceType. '{ChoiceType}'\");\n        }\n    }\n\n    /// \u003csummary\u003e\n    /// Implicitly converts an \u003csee cref=\"AltaSoft.ChoiceGenerator.Tests.Authorisation1Code\"/\u003e to an \u003csee cref=\"Authorisation1Choice\"/\u003e.\n    /// \u003c/summary\u003e\n    /// \u003cparam name=\"value\"\u003eThe \u003csee cref=\"AltaSoft.ChoiceGenerator.Tests.Authorisation1Code\"/\u003e to convert.\u003c/param\u003e\n    /// \u003creturns\u003e\n    /// \u003csee cref=\"Authorisation1Choice\"/\u003e instance representing the code.\n    /// \u003c/returns\u003e\n    public static implicit operator Authorisation1Choice(AltaSoft.ChoiceGenerator.Tests.Authorisation1Code value) =\u003e CreateAsCode(value);\n\n    /// \u003csummary\u003e\n    /// Implicitly converts an \u003csee cref=\"Proprietary\"/\u003e to an \u003csee cref=\"Authorisation1Choice\"/\u003e.\n    /// \u003c/summary\u003e\n    /// \u003cparam name=\"value\"\u003eThe \u003csee cref=\"Proprietary\"/\u003e to convert.\u003c/param\u003e\n    /// \u003creturns\u003e\n    /// \u003csee cref=\"Authorisation1Choice\"/\u003e instance representing the code.\n    /// \u003c/returns\u003e\n    public static implicit operator Authorisation1Choice(Proprietary value) =\u003e CreateAsProprietary(value);\n\n    /// \u003csummary\u003e\n    /// Determines whether the \u003csee cref=\"Code\"/\u003e property should be serialized.\n    /// \u003c/summary\u003e\n    /// \u003creturns\u003e\n    /// \u003cc\u003etrue\u003c/c\u003e if \u003csee cref=\"Code\"/\u003e has a value; otherwise, \u003cc\u003efalse\u003c/c\u003e.\n    /// \u003c/returns\u003e\n    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]\n    public bool ShouldSerializeCode() =\u003e Code.HasValue;\n\n    /// \u003csummary\u003e\n    /// \u003cpara\u003eChoice enumeration\u003c/para\u003e\n    /// \u003c/summary\u003e\n    [XmlType(\"ChoiceOf.Authorisation1Choice\")]\n    public enum ChoiceOf\n    {\n        /// \u003csummary\u003e\n        /// Specifies the authorisation, in a coded form.\n        /// \u003c/summary\u003e\n        Code, \n        /// \u003csummary\u003e\n        /// Specifies the authorisation, in a free text form.\n        /// \u003c/summary\u003e\n        Proprietary, \n    }\n}\n\n```\n---\n\n## 💡 Example Usage\n\n### Creating with CreateAs methods\n```csharp\nvar choice = Authorisation1Choice.CreateAsCode(Authorisation1Code.FileLevelAuthorisationSummary);\n\nvar result = choice.Match(\n    code =\u003e $\"It's a code: {code}\",\n    prop =\u003e $\"It's proprietary: {prop.ToString()}\"\n);\n\nchoice.Switch(\n    code =\u003e Console.WriteLine($\"String: {code}\"),\n    prop =\u003e Console.WriteLine($\"Number: {prop.ToString()}\")\n);\n```\n\n### Creating using implicit operators\n\nif property types are distinct implicit operators are generated\n```csharp\nAuthosiration1Choice choice = Authorisation1Code.FileLevelAuthorisationSummary;\n```\n\n\n\n## 📦 Projects\n\n- `AltaSoft.Choice`  \n  Contains the `[Choice]` marker attribute\n\n- `AltaSoft.Choice.Generator`  \n  Implements the source generator that produces boilerplate code\n\n---\n\n## 📄 License\n\nThis project is licensed under the [MIT License](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faltasoft%2Fchoice","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faltasoft%2Fchoice","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faltasoft%2Fchoice/lists"}