Porticle.Enumly 1.1.0

dotnet add package Porticle.Enumly --version 1.1.0
                    
NuGet\Install-Package Porticle.Enumly -Version 1.1.0
                    
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="Porticle.Enumly" Version="1.1.0">
  <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.
<PackageVersion Include="Porticle.Enumly" Version="1.1.0" />
                    
Directory.Packages.props
<PackageReference Include="Porticle.Enumly">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Porticle.Enumly --version 1.1.0
                    
#r "nuget: Porticle.Enumly, 1.1.0"
                    
#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.
#:package Porticle.Enumly@1.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Porticle.Enumly&version=1.1.0
                    
Install as a Cake Addin
#tool nuget:?package=Porticle.Enumly&version=1.1.0
                    
Install as a Cake Tool

Porticle.Enumly

A Roslyn source generator for compile-time enum-to-enum mapping. Mark a partial class with [EnumlyClass] and partial methods with [EnumlyMap] and Porticle.Enumly fills in the implementation — by member name, with prefix stripping, nullable enum support, explicit value overrides and exhaustive coverage diagnostics.

Build State

Build and Release

NuGet

NuGet Latest Version NuGet Downloads

Installation

dotnet add package Porticle.Enumly

Quick start

using Porticle.Enumly;

[EnumlyClass]
public static partial class Mapper
{
    [EnumlyMap]
    public static partial Foo ToFoo(Bar value);

    [EnumlyMap]
    public static partial Bar ToBar(Foo value);
}

public enum Foo { Red, Green, Blue }
public enum Bar { Green, Red, Blue }

The generator produces a switch-based implementation that maps members by name. If a source value has no matching target, you get a compile-time error (EM0001) — no silent fallthroughs.

Features

By-name matching with prefix stripping

If every member of an enum shares a common prefix that ends at a Pascal-case boundary, that prefix is ignored when matching against the other enum:

public enum Foo { GoldRed, GoldRose, GoldRoyal }   // prefix: "Gold"
public enum Bar { BarRed, BarRose, BarRoyal }      // prefix: "Bar"

// Bar.BarRed   -> Foo.GoldRed
// Bar.BarRose  -> Foo.GoldRose
// Bar.BarRoyal -> Foo.GoldRoyal

The detection looks for the longest common prefix of all member names and trims it back to the last position where every member's next character is uppercase. Source and target are analyzed independently, so prefix stripping works even when only one side has a prefix.

Nullable enum mappings

All four direction combinations work:

[EnumlyMap]
public static partial Foo  ToFoo(Bar value);    // T -> U

[EnumlyMap(NullSourceValue = Bar.None)]
public static partial Foo? ToFoo(Bar value);    // T -> U?  (Bar.None -> null)

[EnumlyMap(NullTargetValue = Foo.Unknown)]
public static partial Foo  ToFoo(Bar? value);   // T? -> U  (null -> Foo.Unknown)

[EnumlyMap]
public static partial Foo? ToFoo(Bar? value);   // T? -> U? (null -> null)
  • NullSourceValue is a value of the source enum that should map to null on the target side. Only meaningful when the target is nullable.
  • NullTargetValue is a value of the target enum that is produced when the source is null.
  • IgnoreNullSource = true — when the source is null, throw ArgumentNullException at runtime instead of producing a target. Use this when you'd rather fail loudly than pick a target value for null.
  • For T? -> U?, null -> null happens automatically. NullSourceValue, NullTargetValue and IgnoreNullSource can still be used to override that.

When the source is nullable and the target is non-nullable, exactly one of NullTargetValue or IgnoreNullSource must be set (otherwise EM0010, error) — null input must have a defined behavior. Setting both is a contradiction and fails with EM0011.

[EnumlyMap(IgnoreNullSource = true)]
public static partial Foo ToFoo(Bar? value);   // T? -> U  (null -> throw)

Explicit value overrides

For special cases where by-name matching can't reach (because the names don't line up even after prefix stripping), use [EnumlyMapValue]:

[EnumlyMap]
[EnumlyMapValue(Bar.BarRoyalBlue, Foo.GoldRoyal)]
public static partial Foo ToFoo(Bar value);

Explicit mappings override by-name matching for the given source value. The attribute can be applied multiple times per method. A source value covered by an [EnumlyMapValue] is exempt from the coverage diagnostic.

Ignoring unmapped values

By default the generator enforces coverage in both directions:

  • every source value must have a target (otherwise EM0001, error)
  • every target value must be reachable from some source (otherwise EM0008, warning)

When a value is intentionally outside the mapping, opt out per-value:

[EnumlyMap]
[EnumlyMapValue(Bar.BarRoyalBlue, Foo.GoldRoyal)]
[EnumlyIgnoreSource(Bar.BarLegacy)]      // suppresses EM0001 for Bar.BarLegacy
[EnumlyIgnoreTarget(Foo.GoldReserved)]   // suppresses EM0008 for Foo.GoldReserved
public static partial Foo ToFoo(Bar value);
  • [EnumlyIgnoreSource(value)] — marks a source value as intentionally unmapped. At runtime, calling the method with that value throws ArgumentOutOfRangeException with a message identifying the value as excluded by [EnumlyIgnoreSource] — distinguishable from a generic unsupported value.
  • [EnumlyIgnoreTarget(value)] — marks a target value as intentionally unreachable. Purely a compile-time hint; no runtime effect.

Both attributes can be applied multiple times. The argument's enum type is verified at compile time (otherwise EM0009, error).

Diagnostics

ID Severity Meaning
EM0001 Error Source enum value has no matching target member (after prefix-strip and explicit overrides). Suppressible per-value with [EnumlyIgnoreSource].
EM0002 Error Method is not partial / has invalid signature (must take one enum and return an enum).
EM0003 Error NullSourceValue is set but the return type is not nullable.
EM0004 Error A specified null value is not a member of its enum.
EM0005 Error A specified null value has the wrong enum type.
EM0006 Error An EnumlyMapValue argument has the wrong enum type.
EM0007 Error The same source value is mapped explicitly more than once.
EM0008 Warning Target enum value is not reachable from any source. Suppressible per-value with [EnumlyIgnoreTarget], or globally via .editorconfig (dotnet_diagnostic.EM0008.severity = none).
EM0009 Error An EnumlyIgnoreSource / EnumlyIgnoreTarget argument has the wrong enum type.
EM0010 Error Source is nullable and target is non-nullable, but neither NullTargetValue nor IgnoreNullSource = true is set — null input has no defined behavior.
EM0011 Error NullTargetValue and IgnoreNullSource are both set — they describe conflicting behavior for null input.

NullTargetValue and IgnoreNullSource on a non-nullable source are allowed and ignored — no diagnostic.

Example: full mapper class

using Porticle.Enumly;

[EnumlyClass]
public static partial class Mapper
{
    [EnumlyMap]
    [EnumlyMapValue(Bar.BarRoyalBlue, Foo.GoldRoyal)]
    public static partial Foo ToFoo(Bar value);

    [EnumlyMap(NullSourceValue = Noo.GooNone)]
    [EnumlyMapValue(Noo.GooRoyal, Bar.BarRoyalBlue)]
    public static partial Bar? ToNullableBar(Noo value);

    [EnumlyMap(NullTargetValue = Noo.GooNone)]
    [EnumlyMapValue(Bar.BarRoyalBlue, Noo.GooRoyal)]
    public static partial Noo ToNoo(Bar? value);
}

The generator produces:

public static partial Foo ToFoo(Bar value)
{
    return value switch
    {
        Bar.BarRed       => Foo.GoldRed,
        Bar.BarRose      => Foo.GoldRose,
        Bar.BarRoyalBlue => Foo.GoldRoyal,           // explicit override
        _ => throw new ArgumentOutOfRangeException(...)
    };
}

License

MIT — see 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.1.0 159 5/5/2026
1.0.0 93 5/5/2026
1.0.0-beta1 88 5/5/2026