VariantEnum 1.0.1

dotnet add package VariantEnum --version 1.0.1                
NuGet\Install-Package VariantEnum -Version 1.0.1                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="VariantEnum" Version="1.0.1">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add VariantEnum --version 1.0.1                
#r "nuget: VariantEnum, 1.0.1"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install VariantEnum as a Cake Addin
#addin nuget:?package=VariantEnum&version=1.0.1

// Install VariantEnum as a Cake Tool
#tool nuget:?package=VariantEnum&version=1.0.1                

VariantEnum

MIT License

VariantEnum is C# Source Generator that automatically creates a Rust Enum-like record class from an Enum, where each variant can have a value.
The automatically created record class makes use of .NET 8 and C# 12 language features (Incremental Generator, ISpanParsable<T>, static abstract interfaces and Collection expressions).
This library is distributed via NuGet.

PM> Install-Package VariantEnum

How to use

If the Enum definition name ends with 'Variant', a record class is automatically created with the same name as the enum definition name without 'Variant'. Enum members can be assigned the [TVariantValueType] attribute so that they have their own specific data.. Values can be accessed as argsXXX.
For example, if an IpAddrVariant Enum is defined:

public enum IpAddrVariant : byte
{
    [VariantValueType(typeof(byte), typeof(byte), typeof(byte), typeof(byte))]
    V4,
    [VariantValueType(typeof(string))]
    V6,
    None
}

This creates IpAddr record class.

var ip = new IpAddr.V4(127, 0, 0, 1);

var value = (IpAddr)ip switch
{
    IpAddr.V4 v4 => $"{v4.args0}.{v4.args1}.{v4.args2}.{v4.args3}",
    IpAddr.V6 v6 => v6.args0,
    _ => throw new Exception(),
};

Console.WriteLine(value); // 127.0.0.1

IpAddr record class automatically generates the following APIs.

public abstract record IpAddr : ISpanFormattable, ISpanParsable<IpAddr>
{
    public sealed record V4(byte args0, byte args1, byte args2, byte args3) : IpAddr
    {
        public static V4 Default => new V4(args0: default, args1: default, args2: default, args3: default);
        public override bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider? provider = null);
    }

    public sealed record V6(string args0) : IpAddr
    {
        public static V6 Default => new V6(args0: default);
        public override bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider? provider = null);
    }

    public sealed record None : IpAddr
    {
        public static None Default => new None();
        public override bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider? provider = null);
    }

    // ISpanFormattable
    public abstract bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider? provider = null);
    public string ToString(string? format, IFormatProvider? formatProvider) => ToString();

    // ISpanParsable<T>
    public static IpAddr Parse(ReadOnlySpan<char> s, IFormatProvider? provider = default);
    public static bool TryParse(ReadOnlySpan<char> s, IFormatProvider? provider, [MaybeNullWhen(false)] out IpAddr result);

    // IParsable<T>
    public static IpAddr Parse(string s, IFormatProvider? provider = default);
    public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, [MaybeNullWhen(false)] out IpAddr result);

    public static int Count { get; } => 3;
    public static string? GetName(IpAddr t); 
    public static string[] GetNames() => [nameof(V4), nameof(V6), nameof(None)];
    public static byte GetNumericValue(IpAddr t);
    public static IpAddrVariant ConvertEnum(IpAddr t);
    public static bool TryConvertEnum([NotNullWhen(true)] IpAddr? t, [MaybeNullWhen(false)] out IpAddrVariant result);
    public static IpAddr Parse(ReadOnlySpan<char> s, bool ignoreCase, IFormatProvider? provider = default);
    public static bool TryParse(ReadOnlySpan<char> s, out IpAddr result);
    public static bool TryParse(ReadOnlySpan<char> s, bool ignoreCase, IFormatProvider? provider, [MaybeNullWhen(false)] out IpAddr result);
    public static bool IsDefined(ReadOnlySpan<char> s);
    public static bool IsDefined([NotNullWhen(true)] IpAddr? value);
}

<details><summary>Generated All Code(IpAddr.g.cs)</summary>


// <auto-generated> This .cs file is generated by VariantEnum. </auto-generated>
#nullable enable
#pragma warning disable CS0219 // The variable 'variable' is assigned but its value is never used
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
#pragma warning disable CS8601 // Possible null reference assignment.
#pragma warning disable CS8602 // Dereference of a possibly null reference.
#pragma warning disable CS8603 // Possible null reference return.
#pragma warning disable CS8604 // Possible null reference argument for parameter.
#pragma warning disable CS8619 // Possible null reference assignment fix

using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;

namespace ConsoleApp;

public abstract record IpAddr : 
    ISpanFormattable, 
    ISpanParsable<IpAddr>
{
    public sealed record V4(byte args0, byte args1, byte args2, byte args3) : IpAddr
    {
        public static V4 Default => new V4(args0: default, args1: default, args2: default, args3: default);

        public override bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider? provider = null)
        {
            var index = 0;
            charsWritten = 0;

            if (destination.Length < 5)
            {
                charsWritten += index;
                return false;
            }
            destination[index++] = 'V';
            destination[index++] = '4';
            destination[index++] = ' ';
            destination[index++] = '{';
            destination[index++] = ' ';
            var handler = new DefaultInterpolatedStringHandler();
            handler.AppendLiteral("args0 = ");
            handler.AppendFormatted(args0);
            handler.AppendFormatted(", " );
            handler.AppendLiteral("args1 = ");
            handler.AppendFormatted(args1);
            handler.AppendFormatted(", " );
            handler.AppendLiteral("args2 = ");
            handler.AppendFormatted(args2);
            handler.AppendFormatted(", " );
            handler.AppendLiteral("args3 = ");
            handler.AppendFormatted(args3);
            var print = handler.ToStringAndClear();
            var printSpan = print.AsSpan();

            if (destination.Length < printSpan.Length + index)
            {
                charsWritten += index;
                return false;
            }
            printSpan.CopyTo(destination.Slice(index, printSpan.Length));
            index += printSpan.Length;
            if (destination.Length < 2 + index)
            {
                charsWritten += index;
                return false;
            }
            destination[index++] = ' ';
            destination[index++] = '}';

            charsWritten = index;
            return true;
        }

    }

    public sealed record V6(string args0) : IpAddr
    {
        public static V6 Default => new V6(args0: default);

        public override bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider? provider = null)
        {
            var index = 0;
            charsWritten = 0;

            if (destination.Length < 5)
            {
                charsWritten += index;
                return false;
            }
            destination[index++] = 'V';
            destination[index++] = '6';
            destination[index++] = ' ';
            destination[index++] = '{';
            destination[index++] = ' ';
            var handler = new DefaultInterpolatedStringHandler();
            handler.AppendLiteral("args0 = ");
            handler.AppendFormatted(args0);
            var print = handler.ToStringAndClear();
            var printSpan = print.AsSpan();

            if (destination.Length < printSpan.Length + index)
            {
                charsWritten += index;
                return false;
            }
            printSpan.CopyTo(destination.Slice(index, printSpan.Length));
            index += printSpan.Length;
            if (destination.Length < 2 + index)
            {
                charsWritten += index;
                return false;
            }
            destination[index++] = ' ';
            destination[index++] = '}';

            charsWritten = index;
            return true;
        }

    }

    public sealed record None : IpAddr
    {
        public static None Default => new None();

        public override bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider? provider = null)
        {
            var index = 0;
            charsWritten = 0;

            if (destination.Length < 8)
            {
                charsWritten += index;
                return false;
            }
            destination[index++] = 'N';
            destination[index++] = 'o';
            destination[index++] = 'n';
            destination[index++] = 'e';
            destination[index++] = ' ';
            destination[index++] = '{';
            destination[index++] = ' ';
            destination[index++] = '}';

            charsWritten = index;
            return true;
        }

    }


    public abstract bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider? provider = null);

    public string ToString(string? format, IFormatProvider? formatProvider) => ToString();

    public static int Count => 3;

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static string? GetName(IpAddr ipaddr)
    {
        return ipaddr switch
        {
            V4 => nameof(V4),
            V6 => nameof(V6),
            None => nameof(None),
            _ => null
        };
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static string[] GetNames() => [nameof(V4), nameof(V6), nameof(None)];

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static byte GetNumericValue(IpAddr ipaddr)
    {
        return ipaddr switch
        {
            V4 => (byte)IpAddrVariant.V4,
            V6 => (byte)IpAddrVariant.V6,
            None => (byte)IpAddrVariant.None,
            _ => ThrowInvalidType<byte>()
        };
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static IpAddrVariant ConvertEnum(IpAddr ipaddr)
    {
        return ipaddr switch
        {
            V4 => IpAddrVariant.V4,
            V6 => IpAddrVariant.V6,
            None => IpAddrVariant.None,
            _ => ThrowInvalidType<IpAddrVariant>()
        };
    }

    public static bool TryConvertEnum([NotNullWhen(true)] IpAddr? ipaddr, [MaybeNullWhen(false)] out IpAddrVariant result)
    {
        switch(ipaddr)
        {
            case V4:
                result = IpAddrVariant.V4;
                return true;
            case V6:
                result = IpAddrVariant.V6;
                return true;
            case None:
                result = IpAddrVariant.None;
                return true;
        }
        result = default;
        return false;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static IpAddr Parse(string s, IFormatProvider? provider = default)
    {
        return Parse(s.AsSpan(), false, provider);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static IpAddr Parse(ReadOnlySpan<char> s, IFormatProvider? provider = default)
    {
        return Parse(s, false, provider);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static IpAddr Parse(ReadOnlySpan<char> s, bool ignoreCase, IFormatProvider? provider = default)
    {
        if (TryParse(s, ignoreCase, provider, out var result))
        {
            return result;
        }
        else
        {
            ThrowRequestedValueNotFound(s);
            return default!;
        }
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, [MaybeNullWhen(false)] out IpAddr result)
    {
        return TryParse(s.AsSpan(), false, provider, out result);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool TryParse(ReadOnlySpan<char> s, out IpAddr result)
    {
        return TryParse(s, false, null, out result);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool TryParse(ReadOnlySpan<char> s, IFormatProvider? provider, [MaybeNullWhen(false)] out IpAddr result)
    {
        return TryParse(s, false, null, out result);
    }

    public static bool TryParse(ReadOnlySpan<char> s, bool ignoreCase, IFormatProvider? provider, [MaybeNullWhen(false)] out IpAddr result)
    {
        if (ignoreCase)
        {
            if (s.Equals(nameof(V4), StringComparison.OrdinalIgnoreCase))
            {
                result = V4.Default;
                return true;
            }
            if (s.Equals(nameof(V6), StringComparison.OrdinalIgnoreCase))
            {
                result = V6.Default;
                return true;
            }
            if (s.Equals(nameof(None), StringComparison.OrdinalIgnoreCase))
            {
                result = None.Default;
                return true;
            }

            result = default;
            return false;
        }
        else
        {
            switch (s)
            {
                case "V4":
                    result = V4.Default;
                    return true;
                case "V6":
                    result = V6.Default;
                    return true;
                case "None":
                    result = None.Default;
                    return true;
            }
            result = default;
            return false;
        }
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool IsDefined(ReadOnlySpan<char> s)
    {
        return s switch
        {
            "V4" => true,
            "V6" => true,
            "None" => true,
            _ => false
        };
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool IsDefined([NotNullWhen(true)] IpAddr? value)
    {
        return value switch
        {
            V4 => true,
            V6 => true,
            None => true,
            _ => false
        };
    }

    [DoesNotReturn]
    private static void ThrowRequestedValueNotFound(ReadOnlySpan<char> s)
    {
        throw new ArgumentException($"Requested value '{s}' was not found.");
    }

    [DoesNotReturn]
    private static T ThrowInvalidType<T>()
    {
        throw new ArgumentException($"Requested value was invalid type.'");
    }
}

</details>

Ignore automatic generation

If automatic generation is to be ignored, you assign the [IgnoreVariant] attribute.

[IgnoreVariant]
public enum TestVariant
{}

Auto-generated API

Parse TryParse

var v4 = IpAddr.Parse("V4");
var v4IgnoreCase = IpAddr.Parse("v4", true);
if (IpAddr.TryParse("V4", out var v4))
{
    Console.WriteLine(v4); // V4 { args0 = 0, args1 = 0, args2 = 0, args3 = 0 }
}

if (IpAddr.TryParse("v4", true, null, out var v4IgnoreCase))
{
    Console.WriteLine(v4IgnoreCase); // V4 { args0 = 0, args1 = 0, args2 = 0, args3 = 0 }
}

Count

var count = IpAddr.Count;
Console.WriteLine(count); // 3

GetName

var name = IpAddr.GetName(new IpAddr.V4(127, 0, 0, 1));
Console.WriteLine(name); // V4

GetNames

var names = IpAddr.GetNames();
foreach (var name in names)
{
    Console.WriteLine(name);
}
// V4
// V6
// None

IsDefined

var result = IpAddr.IsDefined("V4");                            // true
var result2 = IpAddr.IsDefined(new IpAddr.V4(127, 0, 0, 1));    // true

GetNumericValue

var v4Number = IpAddr.GetNumericValue(new IpAddr.V4(127, 0, 0, 1)); // 0
var v6Number = IpAddr.GetNumericValue(new IpAddr.V6("::1"));        // 1

ConvertEnum TryConvertEnum

Get the enumeration value of the original enum from the specified member.

IpAddrVariant variantEnum = IpAddr.ConvertEnum(new IpAddr.V4(127, 0, 0, 1));
    
if (IpAddr.TryConvertEnum(new IpAddr.V4(127, 0, 0, 1), out IpAddrVariant result))
{
}

License

MIT License.

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.0.1 37 1/12/2025