RSCG – ScottEncodingGenerator
| name | ScottEncodingGenerator |
| nuget | https://www.nuget.org/packages/ScottEncodingGenerator/ |
| link | https://github.com/Georgiy-Petrov/ScottEncodingGenerator |
| author | Georgiy Petrov |
Usage
1. You declare a union-like type as an abstract partial class and annotate it with ScottEncoding.
2. Inside it, you define each case as a sealed partial nested class (in this sample: Ok and None).
3. Your business logic returns one of those cases (Ok when save succeeds, None when it does not).
4. Consumer code can handle results either with normal C# pattern matching or with the generated functional matching API.
2. What the generator adds
1. A Match contract on the parent union type, plus per-case implementations that dispatch to the correct handler.
2. A companion module with factory helpers to construct each case (Ok / None) without directly calling constructors.
3. A fluent matching flow (IfOk(…).IfNone(…)) built on top of Match.
4. A generated ScottEncodingAttribute definition, so the attribute is available at compile time.
This is how you can use ScottEncodingGenerator .
The code that you start with is
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ScottEncodingGenerator" Version="1.0.0" />
</ItemGroup>
</Project>
The code that you will use is
using UnionTypesDemo;
Console.WriteLine("Save or not");
ResultSave<int> data = SaveToDatabase.Save(0);
var message= data switch
{
ResultSave<int>.Ok ok => $"Saved {ok.Value}",
ResultSave<int>.None => "Not found",
};
Console.WriteLine(message);
data = SaveToDatabase.Save(1);
message = data.Match(
ok => $"Saved {ok.Value}",
none => "Not found"
);
Console.WriteLine(message);
namespace UnionTypesDemo;
[ScottEncoding]
public abstract partial class ResultSave<T>
{
public sealed partial class Ok
{
public Ok(T value) => Value = value;
public T Value { get; }
}
public sealed partial class None
{
public None() { }
}
}
namespace UnionTypesDemo;
public class SaveToDatabase
{
public static ResultSave<int> Save(int i)
{
if (i == 0)
{
return new ResultSave<int>.None();
}
return new ResultSave<int>.Ok(i);
}
}
The code that is generated is
// <auto-generated />
#nullable enable
namespace UnionTypesDemo
{
public abstract partial class ResultSave<T>
{
public abstract TResult Match<TResult>(global::System.Func<global::UnionTypesDemo.ResultSave<T>.Ok, TResult> ok, global::System.Func<global::UnionTypesDemo.ResultSave<T>.None, TResult> none);
public sealed partial class Ok : global::UnionTypesDemo.ResultSave<T>
{
public override TResult Match<TResult>(global::System.Func<global::UnionTypesDemo.ResultSave<T>.Ok, TResult> ok, global::System.Func<global::UnionTypesDemo.ResultSave<T>.None, TResult> none) => ok(this);
}
public sealed partial class None : global::UnionTypesDemo.ResultSave<T>
{
public override TResult Match<TResult>(global::System.Func<global::UnionTypesDemo.ResultSave<T>.Ok, TResult> ok, global::System.Func<global::UnionTypesDemo.ResultSave<T>.None, TResult> none) => none(this);
}
}
public static partial class ResultSaveModule
{
public static global::UnionTypesDemo.ResultSave<T> Ok<T>(T value) => new global::UnionTypesDemo.ResultSave<T>.Ok(value);
public static global::UnionTypesDemo.ResultSave<T> None<T>() => new global::UnionTypesDemo.ResultSave<T>.None();
public static MatchStep2<T, TResult> IfOk<T, TResult>(this global::UnionTypesDemo.ResultSave<T> value, global::System.Func<global::UnionTypesDemo.ResultSave<T>.Ok, TResult> ok) => new(value, ok);
public readonly struct MatchStep2<T, TResult>
{
private readonly global::UnionTypesDemo.ResultSave<T> _value;
private readonly global::System.Func<global::UnionTypesDemo.ResultSave<T>.Ok, TResult> _ok;
public MatchStep2(global::UnionTypesDemo.ResultSave<T> value, global::System.Func<global::UnionTypesDemo.ResultSave<T>.Ok, TResult> ok)
{
_value = value;
_ok = ok;
}
public TResult IfNone(global::System.Func<global::UnionTypesDemo.ResultSave<T>.None, TResult> none) => _value.Match(_ok, none);
}
}
}
using System;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false, Inherited = false)]
internal sealed class ScottEncodingAttribute : Attribute
{
}
Code and pdf at
https://ignatandrei.github.io/RSCG_Examples/v2/docs/ScottEncodingGenerator