Skip to content

dex3r/EasySourceGenerators

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

86 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

EasySourceGenerators

NuGet MIT License GitHub Repo CI codecov

Easy Source Generators - Code generation made easy.

With this package, you can easily create code that generates source - without creating a separate Analyzers assembly and without learning Roslyn Source Generators. It will be generated any time you run a build.

Just look at the examples below, or scroll down to a more in-depth explanation.

Examples

Simple method generation

public enum ColorsEnum { Red, Green, Blue }

public partial class ColorsClass
{
    public partial string GetAllColorsString();

    [GeneratesMethod(nameof(GetAllColorsString))]
    static string GetAllColorsString_Generator() =>
        string.Join(", ", Enum.GetNames<ColorsEnum>());
}

This generates a method:

public string GetAllColorsString() => "Red, Green, Blue";

Switch-case specialization with fallback

public static partial class PiExample
{
    public static partial int GetPiDecimal(int decimalNumber);

    [GeneratesMethod(nameof(GetPiDecimal))]
    [SwitchCase(0)]
    [SwitchCase(1)]
    [SwitchCase(2)]
    static int GetPiDecimal_Generator_Specialized(int decimalNumber) =>
        SlowMath.CalculatePiDecimal(decimalNumber);

    [GeneratesMethod(nameof(GetPiDecimal))]
    [SwitchDefault]
    static Func<int, int> GetPiDecimal_Generator_Fallback() =>
        decimalNumber => SlowMath.CalculatePiDecimal(decimalNumber);
}

This generates a method:

public static int GetPiDecimal(int decimalNumber)
{
    switch (decimalNumber)
    {
        case 0: return 3;
        case 1: return 1;
        case 2: return 4;
        default: return CalculatePiDecimal(decimalNumber);
    }
}

Fluent API

Instead of using attributes, you can use the fluent API to build more complex behaviour:

public enum FourLegged { Dog, Cat, Lizard }
public enum Mammal { Dog, Cat }

public static partial class MapperFluent
{
    public static partial Mammal MapToMammal(FourLegged fourLegged);

    [GeneratesMethod(nameof(MapToMammal))]
    static IMethodImplementationGenerator MapToAnimal_Generator() =>
        Generate
            .Method().WithParameter<FourLegged>().WithReturnType<Mammal>()
            .WithSwitchBody()
            .ForCases(GetFourLeggedAnimalsThatHasMatchInMammalAnimal()).ReturnConstantValue(fourLegged => Enum.Parse<Mammal>(fourLegged.ToString(), true))
            .ForDefaultCase().UseBody(fourLegged => () => throw new ArgumentException($"Cannot map {fourLegged} to a Mammal"));

    static FourLegged[] GetFourLeggedAnimalsThatHasMatchInMammalAnimal() =>
        Enum
            .GetValues<FourLegged>()
            .Where(fourLeggedAnimal => Enum.TryParse(typeof(Mammal), fourLeggedAnimal.ToString(), true, out _))
            .ToArray();
}

This generates a method:

public static Mammal MapToMammal(FourLegged fourLegged)
{
    switch (fourLegged)
    {
        case FourLegged.Dog: return Mammal.Dog;
        case FourLegged.Cat: return Mammal.Cat;
        default: throw new ArgumentException($"Cannot map {fourLegged} to a Mammal");
    }
}

More examples

See the full example project here: /EasySourceGenerators.Examples.

What it does

You declare partial methods, either static or non-static, and provide generator methods marked with attributes such as:

  • [GeneratesMethod(nameof(YourMethod)]
  • [SwitchCase("someArgumentValue)]
  • [SwitchDefault]

The generator uses Roslyn Source Generators under the hood to generate the source at build time.

For more complex behavior there is a fluent API as well (see MapperFluent example above).

The Generators package and its binaries will not be included in your shipped code. The generators package will be added as a compile-time only dependency:

  <PackageReference Include="EasySourceGenerators.Generators" Version="x.y.z"
                    PrivateAssets="all"
                    IncludeAssets="runtime; build; native; contentfiles; analyzers; buildtransitive" />

Great IDE support

This project uses Roslyn Source Generators under the hood. This gives you great out-of-the-box support from IDEs like Rider, VSCode, and VisualStudio.

You can browse the generated code live in the IL Viewer window of your IDE:

image

Verbose and easy to understand errors

Remove the guesswork from Source Generators.

Roslyn Source Generator projects often produce errors that are hard to understand. One of the main goals of this package is to make sure that any error - whether it’s a user mistake, a setup problem, or an edge case - shows up clearly in your IDE by highlighting the line that caused it.

image

Structure

The project is split into:

  • EasySourceGenerators.Abstractions - attributes and fluent interfaces used in consumer code
  • EasySourceGenerators.Generators - the source generator implementation
  • EasySourceGenerators.Examples - practical usage examples
  • EasySourceGenerators.Tests - tests with Generators as Roslyn Source Generators
  • EasySourceGenerators.GeneratorTests - tests with Generators as assembly references, to cover tests that would be impossible otherwise

About

Code generation made easy. Create code that generates source - without creating a separate Analyzers assembly and without learning Roslyn Source Generators.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages