Category: roslyn

RSCG – PlantUmlClassDiagramGenerator

RSCG – PlantUmlClassDiagramGenerator
 
 

name PlantUmlClassDiagramGenerator
nuget https://www.nuget.org/packages/PlantUmlClassDiagramGenerator.SourceGenerator/
https://www.nuget.org/packages/PlantUmlClassDiagramGenerator.Attributes/
link https://github.com/pierre3/PlantUmlClassDiagramGenerator/
author Hirotada Kobayashi

Generating UML from class definitions

 

This is how you can use PlantUmlClassDiagramGenerator .

The code that you start with is


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <!--<PackageReference Include="PlantUmlClassDiagramGenerator.Attributes" Version="1.3.0.1" />-->
    <PackageReference Include="PlantUmlClassDiagramGenerator.SourceGenerator" Version="0.1.9-alpha">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
  </ItemGroup>

	<PropertyGroup>
	<!--<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">-->
		<DefineConstants>$(DefineConstants);GENERATE_PLANTUML</DefineConstants>
	</PropertyGroup>
	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>
	
	
	
</Project>


The code that you will use is


// See https://aka.ms/new-console-template for more information
using DemoClass2Text;

Person person = new()
{
    FirstName = "Andrei",
    LastName = "Ignat"
};
Console.WriteLine(person.FullName());




namespace DemoClass2Text
{
    [PlantUmlClassDiagramGenerator.SourceGenerator.Attributes.PlantUmlDiagram]
    internal class Person
    {
        public string? FirstName { get; set; }
        public string? LastName { get; set; }
        public string? FullName() => $"{FirstName} {LastName}";
    }
}

 

The code that is generated is

namespace PlantUmlClassDiagramGenerator.SourceGenerator.Attributes;

[System.AttributeUsage(System.AttributeTargets.Class | System.AttributeTargets.Interface | System.AttributeTargets.Enum | System.AttributeTargets.Struct)]
internal class PlantUmlDiagramAttribute : System.Attribute
{ }
@startuml Person
class Person  {
    + FirstName : string? <<get>> <<set>>
    + LastName : string? <<get>> <<set>>
    + FullName() : string?
    + Person()
}
@enduml

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/PlantUmlClassDiagramGenerator

RSCG – CopyTo

RSCG – CopyTo
 
 

name CopyTo
nuget https://www.nuget.org/packages/RhoMicro.CodeAnalysis.CopyToGenerator
link https://github.com/PaulBraetz/RhoMicro.CodeAnalysis
author Paul Braetz

Generating copy to code for properties of a class

 

This is how you can use CopyTo .

The code that you start with is


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="RhoMicro.CodeAnalysis.CopyToGenerator" Version="14.0.2">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
  </ItemGroup>
	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>
</Project>


The code that you will use is


using CopyToDemo;

Person p = new();
p.FirstName = "Andrei";
p.LastName = "Ignat";

Person p2 = new();
p.CopyTo(p2);
Console.WriteLine(p2.FullName); 


namespace CopyToDemo;

[RhoMicro.CodeAnalysis.GenerateCopyTo]
internal partial class Person
{
    public string? FirstName { get; set; }
    public string? LastName { get; set; }
    public string FullName => $"{FirstName} {LastName}";
}


 

The code that is generated is

namespace RhoMicro.CodeAnalysis;

using System;

[global::System.AttributeUsage(AttributeTargets.Class)]
#if GENERATOR
[RhoMicro.CodeAnalysis.GenerateFactory]
#endif
internal sealed partial class GenerateCopyToAttribute : global::System.Attribute { }
// <auto-generated>
// This file was last generated by the RhoMicro.CodeAnalysis.CopyToGenerator on 2/18/2024 10:52:42 AM +02:00
// </auto-generated>
#pragma warning disable
namespace CopyToDemo
{
	partial class Person
	{
		/// <summary>
		/// Copies this instances public properties to another ones.
		/// </summary>
		/// <param name = "target">The instance to copy this instances properties' values to.</param>
		public void CopyTo(Person target)
		{
			if (this == target || target == null)
			{
				return;
			}

			if (AvoidCopy(this, target))
			{
				return;
			}

			target.FirstName = this.FirstName;
			target.LastName = this.LastName;
		}

		/// <summary>
		/// Evaluates whether copying between two instances of <see cref = "Person"/> should be avoided due to equivalence. This can help avoid unnecessary copying or infinite copying in nested recursive relationships.
		/// </summary>
		/// <param name = "a">The first instance to compare.</param>
		/// <param name = "b">The second instance to compare.</param>
		/// <param name = "result">Upon returning, contains <see langword="true"/> if copying between <paramref name = "a"/> and <paramref name = "b"/> should be avoided; otherwise, <see langword="false"/>.</param>
		static partial void AvoidCopy(Person a, Person b, ref global::System.Boolean result);
		/// <summary>
		/// Evaluates whether copying between two instances of <see cref = "Person"/> should be avoided due to equivalence. This can help avoid unnecessary copying or infinite copying in nested recursive relationships.
		/// </summary>
		/// <param name = "a">The first instance to compare.</param>
		/// <param name = "b">The second instance to compare.</param>
		/// <returns><see langword="true"/> if copying between <paramref name = "a"/> and <paramref name = "b"/> should be avoided; otherwise, <see langword="false"/>.</returns>
		static global::System.Boolean AvoidCopy(Person a, Person b)
		{
			{
				var result = false;
				AvoidCopy(a, b, ref result);
				return result;
			}
		}
	}
}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/CopyTo

RSCG – UnionsGenerator

RSCG – UnionsGenerator
 
 

name UnionsGenerator
nuget https://www.nuget.org/packages/RhoMicro.CodeAnalysis.UnionsGenerator
link https://github.com/PaulBraetz/RhoMicro.CodeAnalysis/
author Paul Braetz

Generating Union types for C#

 

This is how you can use UnionsGenerator .

The code that you start with is


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>

	<ItemGroup>
	  <PackageReference Include="RhoMicro.CodeAnalysis.UnionsGenerator" Version="14.0.2">
	    <PrivateAssets>all</PrivateAssets>
	    <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
	  </PackageReference>
	</ItemGroup>
</Project>


The code that you will use is


using UnionTypesDemo;

Console.WriteLine("Save or not");
var data = SaveToDatabase.Save(0);
Console.WriteLine(data.IsValidationError);
data = SaveToDatabase.Save(1);
Console.WriteLine(data.IsSuccess);



using RhoMicro.CodeAnalysis;
namespace UnionTypesDemo;
public record Success(int Value);
public record ValidationError(string Message);

[UnionType<Success>]
[UnionTypeAttribute<ValidationError>]
public partial class ResultSave
{
}





namespace UnionTypesDemo;

public class SaveToDatabase
{
    public static ResultSave Save(int i)
    {
        if(i ==0)
        {
            return new ValidationError(" cannot save 0");
        }
        return new Success(i);
    }
}




 

The code that is generated is

// <auto-generated>
// This file was generated by RhoMicro.CodeAnalysis.UnionsGenerator
// The tool used to generate this code may be subject to license terms;
// this generated code is however not subject to those terms, instead it is
// subject to the license (if any) applied to the containing project.
// </auto-generated>
#nullable enable
#pragma warning disable

namespace RhoMicro.CodeAnalysis
{
    using System;

    /// <summary>
    /// Marks the target type to be related to another union type.
    /// </summary>
    /// <typeparam name="T0">The type to register as related to the target union type.</typeparam>
    [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
#if UNIONS_GENERATOR
    [GenerateFactory(OmitTypeCheck = true)]
#endif
    sealed partial class RelationAttribute<T0> : Attribute
    { }
    /// <summary>
    /// Marks the target type to be related to other union types.
    /// </summary>
    /// <typeparam name="T0">The first type to register as related to the target union type.</typeparam>
    /// <typeparam name="T1">The second type to register as related to the target union type.</typeparam>
    [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
    sealed partial class RelationAttribute<T0, T1> : Attribute
    { }
    /// <summary>
    /// Marks the target type to be related to other union types.
    /// </summary>
    /// <typeparam name="T0">The first type to register as related to the target union type.</typeparam>
    /// <typeparam name="T1">The second type to register as related to the target union type.</typeparam>
    /// <typeparam name="T2">The third type to register as related to the target union type.</typeparam>
    [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
    sealed partial class RelationAttribute<T0, T1, T2> : Attribute
    { }
    /// <summary>
    /// Marks the target type to be related to other union types.
    /// </summary>
    /// <typeparam name="T0">The first type to register as related to the target union type.</typeparam>
    /// <typeparam name="T1">The second type to register as related to the target union type.</typeparam>
    /// <typeparam name="T2">The third type to register as related to the target union type.</typeparam>
    /// <typeparam name="T3">The fourth type to register as related to the target union type.</typeparam>
    [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
    sealed partial class RelationAttribute<T0, T1, T2, T3> : Attribute
    { }
    /// <summary>
    /// Marks the target type to be related to other union types.
    /// </summary>
    /// <typeparam name="T0">The first type to register as related to the target union type.</typeparam>
    /// <typeparam name="T1">The second type to register as related to the target union type.</typeparam>
    /// <typeparam name="T2">The third type to register as related to the target union type.</typeparam>
    /// <typeparam name="T3">The fourth type to register as related to the target union type.</typeparam>
    /// <typeparam name="T4">The fifth type to register as related to the target union type.</typeparam>
    [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
    sealed partial class RelationAttribute<T0, T1, T2, T3, T4> : Attribute
    { }
    /// <summary>
    /// Marks the target type to be related to other union types.
    /// </summary>
    /// <typeparam name="T0">The first type to register as related to the target union type.</typeparam>
    /// <typeparam name="T1">The second type to register as related to the target union type.</typeparam>
    /// <typeparam name="T2">The third type to register as related to the target union type.</typeparam>
    /// <typeparam name="T3">The fourth type to register as related to the target union type.</typeparam>
    /// <typeparam name="T4">The fifth type to register as related to the target union type.</typeparam>
    /// <typeparam name="T5">The sixth type to register as related to the target union type.</typeparam>
    [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
    sealed partial class RelationAttribute<T0, T1, T2, T3, T4, T5> : Attribute
    { }
    /// <summary>
    /// Marks the target type to be related to other union types.
    /// </summary>
    /// <typeparam name="T0">The first type to register as related to the target union type.</typeparam>
    /// <typeparam name="T1">The second type to register as related to the target union type.</typeparam>
    /// <typeparam name="T2">The third type to register as related to the target union type.</typeparam>
    /// <typeparam name="T3">The fourth type to register as related to the target union type.</typeparam>
    /// <typeparam name="T4">The fifth type to register as related to the target union type.</typeparam>
    /// <typeparam name="T5">The sixth type to register as related to the target union type.</typeparam>
    /// <typeparam name="T6">The seventh type to register as related to the target union type.</typeparam>
    [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
    sealed partial class RelationAttribute<T0, T1, T2, T3, T4, T5, T6> : Attribute
    { }
    /// <summary>
    /// Marks the target type to be related to other union types.
    /// </summary>
    /// <typeparam name="T0">The first type to register as related to the target union type.</typeparam>
    /// <typeparam name="T1">The second type to register as related to the target union type.</typeparam>
    /// <typeparam name="T2">The third type to register as related to the target union type.</typeparam>
    /// <typeparam name="T3">The fourth type to register as related to the target union type.</typeparam>
    /// <typeparam name="T4">The fifth type to register as related to the target union type.</typeparam>
    /// <typeparam name="T5">The sixth type to register as related to the target union type.</typeparam>
    /// <typeparam name="T6">The seventh type to register as related to the target union type.</typeparam>
    /// <typeparam name="T7">The eighth type to register as related to the target union type.</typeparam>
    [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
    sealed partial class RelationAttribute<T0, T1, T2, T3, T4, T5, T6, T7> : Attribute
    { }
}

// <auto-generated>
// This file was generated by RhoMicro.CodeAnalysis.UnionsGenerator
// The tool used to generate this code may be subject to license terms;
// this generated code is however not subject to those terms, instead it is
// subject to the license (if any) applied to the containing project.
// </auto-generated>
#nullable enable
#pragma warning disable

namespace RhoMicro.CodeAnalysis
{
    using System;

    /// <summary>
    /// Marks the target method as the factory method to use when instantiating 
    /// an instance of the union type representing a value of the annotated parameter.
    /// Factory methods must be static, have no type parameters and only have one 
    /// parameter of a type representable by the union type. 
    /// Factory polymorphism is not yet supported.
    /// </summary>
    [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
#if UNIONS_GENERATOR
    [GenerateFactory]
#endif
    sealed partial class UnionTypeFactoryAttribute : Attribute
    { }
}

// <auto-generated>
// This file was generated by RhoMicro.CodeAnalysis.UnionsGenerator
// The tool used to generate this code may be subject to license terms;
// this generated code is however not subject to those terms, instead it is
// subject to the license (if any) applied to the containing project.
// </auto-generated>
#nullable enable
#pragma warning disable

namespace RhoMicro.CodeAnalysis.UnionsGenerator.Generated
{
    using System.Collections.Concurrent;
    using System.Collections.Generic;
    using System.Text;
    using System.Linq;
    using System;

    [System.CodeDom.Compiler.GeneratedCodeAttribute("RhoMicro.CodeAnalysis.UnionsGenerator", "14.0.2.0")]
    [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
    internal static class Util
    {
        private readonly static ConcurrentDictionary<Type, String> _cache = new();
        internal static String GetFullString(Type type) => _cache.GetOrAdd(type, ValueFactory);
        static String ValueFactory(Type type)
        {
            var result = getString(type, new());

            return result;

            static String getString(Type type, StringBuilder builder)
            {
                var unboundTransitiveParameters = 0;
                var transitiveParameters = new List<(String Format, Type? Argument)>();
                append(type, builder, transitiveParameters, ref unboundTransitiveParameters);
                var result = builder.ToString();

                for(var i = 0; i < transitiveParameters.Count; i++)
                {
                    _ = builder.Clear();
                    var (format, argument) = transitiveParameters[i];
                    var replacement = getString(argument!, builder);
                    result = result.Replace(format, replacement);
                }

                return result;

                static void append(
                    Type type,
                    StringBuilder builder,
                    List<(String Format, Type? Argument)> transitiveArgumentsMap,
                    ref Int32 unboundTransitiveParameters)
                {
#if NETSTANDARD2_0
                        if(type.IsGenericParameter && type.DeclaringMethod is null)
#else
                    if(type.IsGenericTypeParameter)
#endif
                    {
                        var format = $"{Guid.NewGuid()}";
                        _ = builder.Append(format);
                        transitiveArgumentsMap.Add((format, null));
                        unboundTransitiveParameters++;
                        return;
                    } else if(type.DeclaringType != null)
                    {
                        append(type.DeclaringType, builder, transitiveArgumentsMap, ref unboundTransitiveParameters);
                        _ = builder.Append('.');
                    } else if(type.Namespace != null)
                    {
                        _ = builder.Append(type.Namespace)
                                .Append('.');
                    }

                    var tickIndex = type.Name.IndexOf('`');
                    _ = tickIndex != -1 ?
#if NETSTANDARD2_0
                            builder.Append(type.Name.Substring(0, tickIndex)) :
#else
                        builder.Append(type.Name.AsSpan(0, tickIndex)) :
#endif
                        builder.Append(type.Name);

                    var arguments = type.GetGenericArguments();
                    var inflectionPoint = unboundTransitiveParameters;
                    if(arguments.Length > 0 && unboundTransitiveParameters > 0)
                    {
                        for(; unboundTransitiveParameters > 0;)
                        {
                            unboundTransitiveParameters--;
                            var (format, _) = transitiveArgumentsMap[unboundTransitiveParameters];
                            transitiveArgumentsMap[unboundTransitiveParameters] = (format, arguments[unboundTransitiveParameters]);
                        }
                    }

                    if(arguments.Length > inflectionPoint)
                    {
                        _ = builder.Append('<');
                        append(arguments[inflectionPoint], builder, transitiveArgumentsMap, ref unboundTransitiveParameters);

                        for(var i = inflectionPoint + 1; i < type.GenericTypeArguments.Length; i++)
                        {
                            _ = builder.Append(", ");
                            append(arguments[i], builder, transitiveArgumentsMap, ref unboundTransitiveParameters);
                        }

                        _ = builder.Append('>');
                    }
                }
            }
        }
        
        internal static System.Boolean IsMarked(Type type) =>
            type.CustomAttributes.Any(a => a.AttributeType.FullName == "RhoMicro.CodeAnalysis.UnionTypeAttribute") ||
            type.GenericTypeArguments.Any(t => t.CustomAttributes.Any(a => 
                a.AttributeType.FullName.StartsWith("RhoMicro.CodeAnalysis.UnionTypeAttribute`") 
                && a.AttributeType.GenericTypeArguments.Length < 255));

        private static readonly System.Collections.Concurrent.ConcurrentDictionary<(Type, Type), Object> _conversionImplementations = new();
        internal static TTo UnsafeConvert<TFrom, TTo>(in TFrom from)
        {
            var impl = (System.Func<TFrom, TTo>)_conversionImplementations.GetOrAdd((typeof(TFrom), typeof(TTo)), k =>
            {
                var param = System.Linq.Expressions.Expression.Parameter(k.Item1);
                var castExpr = System.Linq.Expressions.Expression.Convert(param, k.Item2);
                var lambda = System.Linq.Expressions.Expression.Lambda(castExpr, param).Compile();

                return lambda;
            });
            var result = impl.Invoke(from);

            return result;
        }
    }
}
// <auto-generated>
// This file was generated by RhoMicro.CodeAnalysis.UnionsGenerator
// The tool used to generate this code may be subject to license terms;
// this generated code is however not subject to those terms, instead it is
// subject to the license (if any) applied to the containing project.
// </auto-generated>
#nullable enable
#pragma warning disable

namespace RhoMicro.CodeAnalysis
{
    using System;
    using System.Collections.Generic;

    /// <summary>
    /// Defines options for generating union types.
    /// </summary>
    [Flags]
    enum UnionTypeOptions
    {
        /// <summary>
        /// The default options.
        /// </summary>
        Default = ImplicitConversionIfSolitary,
        /// <summary>
        /// </summary>
        None = 0x00,
        /// <summary>
        /// Instructs the generator to emit an implicit conversion to the representable type if it is the only one.
        /// In effect, this option will enable the union type to act as an alias wrapper for the representable type.
        /// </summary>
        ImplicitConversionIfSolitary = 0x01,
        /// <summary>
        /// Instructs the generator to emit a superset conversion operator implementation even though
        /// the representable type is a generic type parameter. By default, it is omitted because of possible
        /// unification for certain generic arguments.
        /// </summary>
        //SupersetOfParameter = 0x02,
        /// <summary>
        /// Instructs the generator to treat the representable reference type 
        /// as nullable, allowing for <see langword="null"/> 
        /// arguments in factories, conversions etc.
        /// </summary>
        Nullable = 0x04
    }

    /// <summary>
    /// Defines options for the storage implementation of a representable type.
    /// In order for the generator to generate an efficient storage implementation, 
    /// consumers should communicate whether the representable type is known to
    /// be a struct, class or of unknown nature. This is mostly relevant for generic
    /// type parameters, however an explicit strategy may be selected for any representable
    /// type. Whether or not generic type parameters are known to be reference
    /// or value types depends on their constraints. Parameters constrained to 
    /// <see langword="struct"/> will be assumed to be value types. Conversely,
    /// parameters constrained to <see langword="class"/> will be assumed to be reference types.
    /// </summary>
    /*
               | box |value| auto | field
        struct | rc! | vc  | vc   | cc
        class  | rc  | rc! | rc   | cc
        none   | rc! | vc! | rc!  | cc
    */
    enum StorageOption
    {
        /// <summary>
        /// The generator will automatically decide on a storage strategy.
        /// <para>
        /// If the representable type is <b>known to be a value type</b>,
        /// this will store values of that type inside a shared value type container.
        /// <b>Boxing will not occur.</b>
        /// </para>
        /// <para>
        /// If the representable type is <b>known to be a reference type</b>,
        /// this will store values of that type inside a shared reference type container.
        /// </para>
        /// <para>
        /// If the representable type is <b>neither known to be a reference type
        /// nor a value type</b>, this option will cause values of that type to 
        /// be stored inside a shared reference type container.
        /// <b>If the representable type is a generic type parameter,
        /// boxing will occur for value type arguments to that parameter.</b>
        /// </para>
        /// </summary>
        Auto,

        /// <summary>
        /// The generator will always store values of the representable type
        /// inside a shared reference type container.
        /// <para>
        /// If the representable type is <b>known to be a value type</b>,
        /// <b>boxing will occur</b>.
        /// </para>
        /// <para>
        /// If the representable type is a <b>generic type parameter</b>,
        /// <b>boxing will occur for value type arguments</b> to that parameter.
        /// </para>
        /// </summary>
        Reference,

        /// <summary>
        /// The generator will attempt to store values of the representable type
        /// inside a value type container.
        /// <para>
        /// If the representable type is <b>known to be a value type</b>,
        /// this will store values of that type inside a shared value type container.
        /// <b>Boxing will not occur.</b>
        /// </para>
        /// <para>
        /// If the representable type is <b>known to be a reference type</b>,
        /// this will store values of that type inside a shared reference type container.
        /// <b>Boxing will not occur.</b>
        /// </para>
        /// <para>
        /// If the representable type is <b>neither known to be a reference type
        /// nor a value type</b>, this option will cause values of that type to 
        /// be stored inside a shared value type container.
        /// <b>If the representable type is a generic type parameter,
        /// an exception of type <see cref="TypeLoadException"/> will occur for
        /// reference type arguments to that parameter.</b>
        /// </para>
        /// </summary>
        Value,

        /// <summary>
        /// The generator will attempt to store values of the representable type
        /// inside a dedicated container for that type.
        /// <para>
        /// If the representable type is <b>known to be a value type</b>,
        /// this will store values of that type inside a dedicated 
        /// value type container.
        /// <b>Boxing will not occur.</b>
        /// </para>
        /// <para>
        /// If the representable type is <b>known to be a reference type</b>,
        /// this will store values of that type inside a 
        /// dedicated reference type container.
        /// </para>
        /// <para>
        /// If the representable type is <b>neither known to be a reference type
        /// nor a value type</b>, this option will cause values of that type to 
        /// be stored inside a dedicated strongly typed container.
        /// <b>Boxing will not occur.</b>
        /// </para>
        /// </summary>
        Field
    }

    /// <summary>
    /// Marks the target type as a union type being able to represent the type passed to the constructor.
    /// </summary>
    [AttributeUsage(( (AttributeTargets)( -1 ) ))]
    partial class UnionTypeBaseAttribute : Attribute
    {
        /// <summary>
        /// Gets or sets the alias groups that the representable type is to be a part of. 
        /// Represnetable types that share a group may be checked for using unified methods 
        /// and properties like <c>IsGroup</c> where <c>Group</c> is the name of the group
        /// that the representable type is a part of.
        /// </summary>
        public virtual String[] Groups { get; set; } = Array.Empty<String>();

        /// <summary>
        /// Gets or sets the generator options to use.
        /// </summary>
        public virtual UnionTypeOptions Options { get; set; } = UnionTypeOptions.Default;

        /// <summary>
        /// Gets or sets the option defining storage generation.
        /// </summary>
        public virtual StorageOption Storage { get; set; }
    }
    [AttributeUsage(( (AttributeTargets)( -1 ) ))]
#if UNIONS_GENERATOR
    [GenerateFactory(OmitTypeCheck = true)]
#endif
    partial class AliasedUnionTypeBaseAttribute : UnionTypeBaseAttribute
    {
        /// <summary>
        /// Gets or sets the alias to use for members representing the type represented by the union.
        /// For example, the represented type <see cref="List{T}"/> would be represented using names like
        /// <c>list_of_T</c>. Setting this property to <c>yourAlias</c> will instruct the generator to use
        /// member names like <c>yourAlias</c> instead of <c>list_of_T</c>. Use this property to avoid
        /// name collisions in generated code. Since the alias will be used for member names, it will
        /// only be taken into account if it is a valid identifier name.
        /// </summary>
        public String? Alias { get; set; }
        /// <inheritdoc/>
        public override String[] Groups { get => base.Groups; set => base.Groups = value; }
        /// <inheritdoc/>
        public override UnionTypeOptions Options { get => base.Options; set => base.Options = value; }
        /// <inheritdoc/>
        public override StorageOption Storage { get => base.Storage; set => base.Storage = value; }
    }
    /// <summary>
    /// Marks the target type as a union type being able to represent <typeparamref name="T0"/>.
    /// </summary>
    [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
    sealed partial class UnionTypeAttribute<T0> : AliasedUnionTypeBaseAttribute
    { }
    /// <summary>
    /// Marks the target type as a union type being able to represent 
    /// <typeparamref name="T0"/>
    /// and <typeparamref name="T1"/>.
    /// </summary>
    [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
    sealed partial class UnionTypeAttribute<T0, T1> : UnionTypeBaseAttribute
    { }
    /// <summary>
    /// Marks the target type as a union type being able to represent 
    /// <typeparamref name="T0"/>,
    /// <typeparamref name="T1"/>
    /// and <typeparamref name="T2"/>.
    /// </summary>
    [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
    sealed partial class UnionTypeAttribute<T0, T1, T2> : UnionTypeBaseAttribute
    { }
    /// <summary>
    /// Marks the target type as a union type being able to represent 
    /// <typeparamref name="T0"/>,
    /// <typeparamref name="T1"/>,
    /// <typeparamref name="T2"/>
    /// and <typeparamref name="T3"/>.
    /// </summary>
    [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
    sealed partial class UnionTypeAttribute<T0, T1, T2, T3> : UnionTypeBaseAttribute
    { }
    /// <summary>
    /// Marks the target type as a union type being able to represent 
    /// <typeparamref name="T0"/>,
    /// <typeparamref name="T1"/>,
    /// <typeparamref name="T2"/>,
    /// <typeparamref name="T3"/>
    /// and <typeparamref name="T4"/>.
    /// </summary>
    [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
    sealed partial class UnionTypeAttribute<T0, T1, T2, T3, T4> : UnionTypeBaseAttribute
    { }
    /// <summary>
    /// Marks the target type as a union type being able to represent 
    /// <typeparamref name="T0"/>,
    /// <typeparamref name="T1"/>,
    /// <typeparamref name="T2"/>,
    /// <typeparamref name="T3"/>,
    /// <typeparamref name="T4"/>
    /// and <typeparamref name="T5"/>.
    /// </summary>
    [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
    sealed partial class UnionTypeAttribute<T0, T1, T2, T3, T4, T5> : UnionTypeBaseAttribute
    { }
    /// <summary>
    /// Marks the target type as a union type being able to represent 
    /// <typeparamref name="T0"/>,
    /// <typeparamref name="T1"/>,
    /// <typeparamref name="T2"/>,
    /// <typeparamref name="T3"/>,
    /// <typeparamref name="T4"/>,
    /// <typeparamref name="T5"/>
    /// and <typeparamref name="T6"/>.
    /// </summary>
    [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
    sealed partial class UnionTypeAttribute<T0, T1, T2, T3, T4, T5, T6> : UnionTypeBaseAttribute
    { }
    /// <summary>
    /// Marks the target type as a union type being able to represent 
    /// <typeparamref name="T0"/>,
    /// <typeparamref name="T1"/>,
    /// <typeparamref name="T2"/>,
    /// <typeparamref name="T3"/>,
    /// <typeparamref name="T4"/>,
    /// <typeparamref name="T5"/>,
    /// <typeparamref name="T6"/>
    /// and <typeparamref name="T7"/>.
    /// </summary>
    [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
    sealed partial class UnionTypeAttribute<T0, T1, T2, T3, T4, T5, T6, T7> : UnionTypeBaseAttribute
    { }
    /// <summary>
    /// Marks the target type as a union type being able to represent the annotated type parameter.
    /// </summary>
    [AttributeUsage(AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
    sealed partial class UnionTypeAttribute : AliasedUnionTypeBaseAttribute
    { }
}
// <auto-generated>
// This file was generated by RhoMicro.CodeAnalysis.UnionsGenerator
// The tool used to generate this code may be subject to license terms;
// this generated code is however not subject to those terms, instead it is
// subject to the license (if any) applied to the containing project.
// </auto-generated>
#nullable enable
#pragma warning disable

namespace RhoMicro.CodeAnalysis;

using System;

#region Setting Enums
/// <summary>
/// Defines settings for generating an implementation of <see cref="Object.ToString"/>.
/// </summary>
enum ToStringSetting
{
    /// <summary>
    /// The generator will emit an implementation that returns detailed information, including:
    /// <list type="bullet">
    /// <item><description>the name of the union type</description></item>
    /// <item><description>a list of types representable by the union type</description></item>
    /// <item><description>an indication of which type is being represented by the instance</description></item>
    /// <item><description>the value currently being represented by the instance</description></item>
    /// </list>
    /// </summary>
    Detailed,
    /// <summary>
    /// The generator will not generate an implementation of <see cref="Object.ToString"/>.
    /// </summary>
    None,
    /// <summary>
    /// The generator will generate an implementation that returns the result of calling <see cref="Object.ToString"/> on the currently represented value.
    /// </summary>
    Simple
}
/// <summary>
/// Defines settings for annotating the target with an instance of <see cref="System.Runtime.InteropServices.StructLayoutAttribute"/>.
/// </summary>
enum LayoutSetting
{
    /// <summary>
    /// Generate an annotation optimized for size.
    /// </summary>
    Small,
    /// <summary>
    /// Do not generate any annotations.
    /// </summary>
    Auto
}
/// <summary>
/// Defines settings for controlling the accessibility of generated constructors.
/// </summary>
enum ConstructorAccessibilitySetting
{
    /// <summary>
    /// Generated constructors should always be private, unless
    /// no conversion operators are generated for the type they
    /// accept. This would be the case for interface types or
    /// supertypes of the target union.
    /// </summary>
    PublicIfInconvertible,
    /// <summary>
    /// Generated constructors should always be private.
    /// </summary>
    Private,
    /// <summary>
    /// Generated constructors should always be public
    /// </summary>
    Public
}
/// <summary>
/// Defines settings on how to implement interfaces that all representable
/// types implement.
/// </summary>
enum InterfaceMatchSetting
{
    /// <summary>
    /// Generated interface implementations should be explicit if at least
    /// one of the representable types implements the interface explicitly;
    /// otherwise, interface implementations should be implicit.
    /// </summary>
    Auto,
    /// <summary>
    /// Generated interface implementations should always be explicit.
    /// </summary>
    Explicit,
    /// <summary>
    /// Generated interface implementations should always be implicit.
    /// </summary>
    Implicit,
    /// <summary>
    /// No interfaces implementations should be generated.
    /// </summary>
    Omit
}
/// <summary>
/// Defines settings for the kind of diagnostics to report.
/// </summary>
[Flags]
enum DiagnosticsLevelSettings
{
    /// <summary>
    /// Instructs the analyzer not to emit diagnostics
    /// </summary>
    None = 0x00,
    /// <summary>
    /// Instructs the analyzer to report info diagnostics.
    /// </summary>
    Info = 0x01,
    /// <summary>
    /// Instructs the analyzer to report warning diagnostics.
    /// </summary>
    Warning = 0x02,
    /// <summary>
    /// Instructs the analyzer to report error diagnostics.
    /// </summary>
    Error = 0x04,
    /// <summary>
    /// Instructs the analyzer to report all diagnostics.
    /// </summary>
    All = Info | Warning | Error
}
/// <summary>
/// Defines miscellaneous settings.
/// </summary>
[Flags]
enum MiscellaneousSettings
{
    /// <summary>
    /// </summary>
    None = 0x00,
    /// <summary>
    /// The default settings.
    /// </summary>
    Default = None,
    /// <summary>
    /// Indicates whether the generated source code should be available as a string constant on the union type itself.
    /// This setting is generally only useful if the generated implementation should be emitted from another generator.
    /// </summary>
    EmitGeneratedSourceCode = 0x01,
    /// <summary>
    /// Indicates whether to generate a custom converter type 
    /// for <c>System.Text.Json</c> deserialization. If set, this will also cause
    /// the union type to be annotated with an appropriate <c>JsonConverter</c> attribute.
    /// </summary>
    GenerateJsonConverter = 0x02,
    /// <summary>
    /// Indicates that the generator should emit a comment detailing the structure of the union type.
    /// </summary>
    EmitStructuralRepresentation = 0x04
}

/// <summary>
/// Defines settings pertaining to equality operator implementations.
/// </summary>
enum EqualityOperatorsSetting
{
    /// <summary>
    /// Equality operators will be emitted only if the target union type is a value type.
    /// </summary>
    EmitOperatorsIfValueType,
    /// <summary>
    /// Equality operators will be emitted.
    /// </summary>
    EmitOperators,
    /// <summary>
    /// Equality operators will be omitted.
    /// </summary>
    OmitOperators
}

#endregion
#region Attribute Declaration
/// <summary>
/// Supplies the generator with additional settings on how to generate a targeted union type.
/// If the target member is an assembly, the attribute supplies default values for any union 
/// type setting not defined.
/// </summary>
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Assembly, AllowMultiple = false, Inherited = false)]
#if UNIONS_GENERATOR
[GenerateFactory]
#endif
sealed partial class UnionTypeSettingsAttribute : Attribute
{
    #region Settings
    /// <summary>
    /// Defines how to generate an implementation <see cref="Object.ToString"/>.
    /// </summary>
    public ToStringSetting ToStringSetting { get; set; } = ToStringSetting.Detailed;
    /// <summary>
    /// Defines whether to generate a size optimizing annotation.
    /// </summary>
    public LayoutSetting Layout { get; set; } = LayoutSetting.Auto;
    /// <summary>
    /// The level of diagnostics to be reported by the analyzer.
    /// </summary>
    public DiagnosticsLevelSettings DiagnosticsLevel { get; set; } = DiagnosticsLevelSettings.All;
    /// <summary>
    /// The desired accessibility of generated constructors.
    /// </summary>
    public ConstructorAccessibilitySetting ConstructorAccessibility { get; set; } = ConstructorAccessibilitySetting.Private;
    /// <summary>
    /// Indicates how to generate implementations for
    /// interfaces implemented by all representable types. Implementations will 
    /// map calls to interface instance methods and properties onto the represented
    /// value. 
    /// <para>
    /// Please note that currently, only fully bound and constructed interface implementations are supported.
    /// </para>
    /// </summary>
    public InterfaceMatchSetting InterfaceMatchSetting { get; set; } = InterfaceMatchSetting.Auto;
    /// <summary>
    /// Indicates how to generate equality operators.
    /// By default, equality operators will only be emitted for value types, so as to preserve
    /// reference equality for comparing reference union types via <c>==</c> or <c>!=</c>.
    /// </summary>
    public EqualityOperatorsSetting EqualityOperatorsSetting { get; set; } = EqualityOperatorsSetting.EmitOperatorsIfValueType;
    /// <summary>
    /// Gets or sets miscellaneous settings.
    /// </summary>
    public MiscellaneousSettings Miscellaneous { get; set; } = MiscellaneousSettings.Default;
    #endregion
    #region Strings
    /// <summary>
    /// A raw code preface to prepend before the generated type declaration.
    /// </summary>
    public String TypeDeclarationPreface { get; set; } = "";
    /// <summary>
    /// The name of the generic parameter for generic <c>Is</c>, <c>As</c> and factory methods. 
    /// Set this property in order to avoid name collisions with generic union type parameters
    /// </summary>
    public String GenericTValueName { get; set; } = "TValue";
    /// <summary>
    /// The name of the generic parameter for the <c>TryConvert</c> method. 
    /// Set this property in order to avoid name collisions with generic union type parameters
    /// </summary>
    public String TryConvertTypeName { get; set; } = "TUnion";
    /// <summary>
    /// The name of the generic parameter for the <c>Match</c> method. 
    /// Set this property in order to avoid name collisions with generic union type parameters
    /// </summary>
    public String MatchTypeName { get; set; } = "TMatchResult";
    /// <summary>
    /// The name to use for the discriminating tag type.
    /// </summary>
    public String TagTypeName { get; set; } = "__Tag";
    /// <summary>
    /// The name to use for the container type containing value types.
    /// </summary>
    public String ValueTypeContainerTypeName { get; set; } = "__ValueTypeContainer";
    /// <summary>
    /// The name to use for the field containing value types.
    /// </summary>
    public String ValueTypeContainerName { get; set; } = "__value";
    /// <summary>
    /// The name to use for the field containing reference types.
    /// </summary>
    public String ReferenceTypeContainerName { get; set; } = "__reference";
    /// <summary>
    /// The name to use for the field containing the discriminating tag.
    /// </summary>
    public String TagFieldName { get; set; } = "__tag";
    /// <summary>
    /// The name to use for the default (uninitialized) tag value.
    /// </summary>
    public String TagNoneName { get; set; } = "__None";
    /// <summary>
    /// The name of the generated json converter type.
    /// </summary>
    public String JsonConverterTypeName { get; set; } = "JsonConverter";
    #endregion
}
#endregion
// <auto-generated>
// This file was last generated by RhoMicro.CodeAnalysis.UnionsGenerator at 2/18/2024 10:51:57 AM +02:00
// The tool used to generate this code may be subject to license terms;
// this generated code is however not subject to those terms, instead it is
// subject to the license (if any) applied to the containing project.
// </auto-generated>
#pragma warning disable
#nullable enable
#region Implementation of UnionTypesDemo.ResultSave
namespace UnionTypesDemo
{
	using System.Linq;
	
	#region Scoped Data
	file static class UnionTypesDemo_ResultSave_ScopedData
	{
		public static System.Collections.Concurrent.ConcurrentDictionary<System.Type, System.Object> Cache { get; } = new();
		public static System.Collections.Generic.HashSet<System.Type> RepresentableTypes { get; } = 
		new ()
		{
			typeof(UnionTypesDemo.Success),
			typeof(UnionTypesDemo.ValidationError)
		}
		;
	}
	#endregion
	partial class ResultSave : System.IEquatable<ResultSave?>
	{
		
		#region Nested Types
		#region Value Type Container
		#endregion
		#region Tag Type
		/// <summary>
		/// Defines tags to discriminate between representable types.
		/// </summary>
		/// <remarks>
		/// This member is not intended for use by user code inside of or any code outside of <see cref="UnionTypesDemo.ResultSave"/>.
		/// </remarks>
		[System.CodeDom.Compiler.GeneratedCodeAttribute("RhoMicro.CodeAnalysis.UnionsGenerator", "14.0.2.0")]
		[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
		private enum __Tag : System.Byte
		{
			/// <summary>
			/// Used when not representing any type due to e.g. incorrect or missing initialization.
			/// </summary>
			__None,

			
			/// <summary>
			/// Used when representing an instance of <see cref="UnionTypesDemo.Success"/>.
			/// </summary>
			Success,
			
			/// <summary>
			/// Used when representing an instance of <see cref="UnionTypesDemo.ValidationError"/>.
			/// </summary>
			ValidationError
		}
		#endregion
		#endregion
		
		#region Constructors
		
		/// <summary>
		/// Creates a new instance of <see cref="UnionTypesDemo.ResultSave"/>representing an instance of <see cref="UnionTypesDemo.Success"/>.
		/// </summary>
		[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
		[System.CodeDom.Compiler.GeneratedCodeAttribute("RhoMicro.CodeAnalysis.UnionsGenerator", "14.0.2.0")]
		private ResultSave(UnionTypesDemo.Success value)
		{
			__tag = __Tag.Success;
			this.__reference = value;
		}
		
		
		/// <summary>
		/// Creates a new instance of <see cref="UnionTypesDemo.ResultSave"/>representing an instance of <see cref="UnionTypesDemo.ValidationError"/>.
		/// </summary>
		[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
		[System.CodeDom.Compiler.GeneratedCodeAttribute("RhoMicro.CodeAnalysis.UnionsGenerator", "14.0.2.0")]
		private ResultSave(UnionTypesDemo.ValidationError value)
		{
			__tag = __Tag.ValidationError;
			this.__reference = value;
		}
		#endregion
		
		#region Fields
		
		/// <summary>
		/// Contains the value of instances of <see cref="UnionTypesDemo.ResultSave"/> representing one of these types:
		/// <list type="bullet">
		/// <item>
		/// <see cref="UnionTypesDemo.Success"/>
		/// </item>
		/// <item>
		/// <see cref="UnionTypesDemo.ValidationError"/>
		/// </item>
		/// </list>
		/// </summary>
		/// <remarks>
		/// This member is not intended for use by user code inside of or any code outside of <see cref="UnionTypesDemo.ResultSave"/>.
		/// </remarks>
		[System.CodeDom.Compiler.GeneratedCodeAttribute("RhoMicro.CodeAnalysis.UnionsGenerator", "14.0.2.0")]
		[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
		private readonly System.Object? __reference;
		
		/// <summary>
		/// Used to determine the currently represented type and value.
		/// </summary>
		/// <remarks>
		/// This member is not intended for use by user code inside of or any code outside of <see cref="UnionTypesDemo.ResultSave"/>.
		/// </remarks>
		[System.CodeDom.Compiler.GeneratedCodeAttribute("RhoMicro.CodeAnalysis.UnionsGenerator", "14.0.2.0")]
		[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
		private readonly __Tag __tag;
		#endregion
		
		#region Factories
		
		/// <summary>
		/// Creates a new instance of <see cref="UnionTypesDemo.ResultSave"/> representing an instance of <see cref="UnionTypesDemo.Success"/>.
		/// </summary>
		/// <param name="value">
		/// The value to be represented by the new instance of <see cref="UnionTypesDemo.ResultSave"/>.
		/// </param>
		/// <returns>
		/// A new instance of <see cref="UnionTypesDemo.ResultSave"/> representing <paramref name="value"/>.
		/// </returns>
		public static ResultSave CreateFromSuccess([RhoMicro.CodeAnalysis.UnionTypeFactory]UnionTypesDemo.Success value) => new(value);
		
		/// <summary>
		/// Creates a new instance of <see cref="UnionTypesDemo.ResultSave"/> representing an instance of <see cref="UnionTypesDemo.ValidationError"/>.
		/// </summary>
		/// <param name="value">
		/// The value to be represented by the new instance of <see cref="UnionTypesDemo.ResultSave"/>.
		/// </param>
		/// <returns>
		/// A new instance of <see cref="UnionTypesDemo.ResultSave"/> representing <paramref name="value"/>.
		/// </returns>
		public static ResultSave CreateFromValidationError([RhoMicro.CodeAnalysis.UnionTypeFactory]UnionTypesDemo.ValidationError value) => new(value);
		/// <summary>
		/// Attempts to create an instance of <see cref="UnionTypesDemo.ResultSave"/> from an instance of <typeparamref name="TValue"/>.
		/// </summary>
		/// <param name="value">
		/// The value from which to attempt to create an instance of <see cref="UnionTypesDemo.ResultSave"/>.
		/// </param>
		/// <param name="result">
		/// If an instance of <see cref="UnionTypesDemo.ResultSave"/> could successfully be created, this parameter will contain the newly created instance; otherwise, <see langword="default"/>.
		/// </param>
		/// <returns>
		/// <see langword="true"/> if an instance of <see cref="UnionTypesDemo.ResultSave"/> could successfully be created; otherwise, <see langword="false"/>.
		/// </returns>
		public static System.Boolean TryCreate<TValue>(TValue value, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out ResultSave? result)
		{
			var metadataName = RhoMicro.CodeAnalysis.UnionsGenerator.Generated.Util.GetFullString(typeof(TValue));
			switch(metadataName)
			{
				case "UnionTypesDemo.Success":
				{
					result = CreateFromSuccess((RhoMicro.CodeAnalysis.UnionsGenerator.Generated.Util.UnsafeConvert<TValue, UnionTypesDemo.Success>(value)));
					return true;
				}
				
				case "UnionTypesDemo.ValidationError":
				{
					result = CreateFromValidationError((RhoMicro.CodeAnalysis.UnionsGenerator.Generated.Util.UnsafeConvert<TValue, UnionTypesDemo.ValidationError>(value)));
					return true;
				}
				default:
				{
					
					{
						var sourceType = typeof(TValue);
						if(!UnionTypesDemo_ResultSave_ScopedData.Cache.TryGetValue(sourceType, out var weakMatch))
						{
							if(!RhoMicro.CodeAnalysis.UnionsGenerator.Generated.Util.IsMarked(sourceType))
							{
								result = default;
								return false;
							}
							weakMatch = UnionTypesDemo_ResultSave_ScopedData.Cache.GetOrAdd(sourceType, t =>
							{
								var tupleType = typeof(System.ValueTuple<System.Boolean, ResultSave>);
								var matchMethod = sourceType.GetMethod(nameof(Match), System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public)
									?.MakeGenericMethod(tupleType) ??
									throw new System.InvalidOperationException("Unable to locate match function on source union type. This indicates a bug in the marker detection algorithm.");
								var targetFactoryMap = typeof(UnionTypesDemo.ResultSave).GetMethods()
									.Where(c => c.CustomAttributes.Any(a => a.AttributeType.FullName == "RhoMicro.CodeAnalysis.UnionTypeFactoryAttribute"))
									.ToDictionary(c => c.GetParameters()[0].ParameterType);
								var handlers = matchMethod.GetParameters()
									.Select(p => p.ParameterType.GenericTypeArguments[0])
									.Select(t => (ParameterExpr: System.Linq.Expressions.Expression.Parameter(t), ParameterExprType: t))
									.Select(t =>
									{
										var delegateType = typeof(System.Func<,>).MakeGenericType(t.ParameterExprType, tupleType);
										System.Linq.Expressions.Expression expression = targetFactoryMap.TryGetValue(t.ParameterExprType, out var factory)
											? System.Linq.Expressions.Expression.New(tupleType.GetConstructors()[0], System.Linq.Expressions.Expression.Constant(true), System.Linq.Expressions.Expression.Call(factory, t.ParameterExpr))
											: System.Linq.Expressions.Expression.Default(tupleType);
										return System.Linq.Expressions.Expression.Lambda(delegateType, expression, t.ParameterExpr);
									}
									);var paramExpr = System.Linq.Expressions.Expression.Parameter(sourceType);
								var callExpr = System.Linq.Expressions.Expression.Call(paramExpr, matchMethod, handlers);
								var lambdaExpr = System.Linq.Expressions.Expression.Lambda(callExpr, paramExpr);
								var result = lambdaExpr.Compile();
								return result;
							}
							);
						}
						var match = (System.Func<TValue, (System.Boolean, UnionTypesDemo.ResultSave)>)weakMatch;
						var matchResult = match.Invoke(value);
						if(!matchResult.Item1)
						{
							result = default;
							return false;
						}
						result = matchResult.Item2;
						return true;
					}
				}
			}
		}
		/// <summary>
		/// Creates an instance of <see cref="UnionTypesDemo.ResultSave"/> from an instance of <typeparamref name="TValue"/>.
		/// </summary>
		/// <param name="value">
		/// The value from which to create an instance of <see cref="UnionTypesDemo.ResultSave"/>.
		/// </param>
		/// <returns>
		/// A new instance of <see cref="UnionTypesDemo.ResultSave"/> representing <paramref name="value"/>.
		/// </returns>
		public static ResultSave Create<TValue>(TValue value)
		{
			var metadataName = RhoMicro.CodeAnalysis.UnionsGenerator.Generated.Util.GetFullString(typeof(TValue));
			switch(metadataName)
			{
				case "UnionTypesDemo.Success":
				{
					return CreateFromSuccess((RhoMicro.CodeAnalysis.UnionsGenerator.Generated.Util.UnsafeConvert<TValue, UnionTypesDemo.Success>(value)));
				}
				
				case "UnionTypesDemo.ValidationError":
				{
					return CreateFromValidationError((RhoMicro.CodeAnalysis.UnionsGenerator.Generated.Util.UnsafeConvert<TValue, UnionTypesDemo.ValidationError>(value)));
				}
				default:
				{
					
					{
						var sourceType = typeof(TValue);
						if(!UnionTypesDemo_ResultSave_ScopedData.Cache.TryGetValue(sourceType, out var weakMatch))
						{
							if(!RhoMicro.CodeAnalysis.UnionsGenerator.Generated.Util.IsMarked(sourceType))
							{
								throw new System.InvalidOperationException($"Unable to create an instance of UnionTypesDemo.ResultSave from an instance of {typeof(TValue)}.");
							}
							weakMatch = UnionTypesDemo_ResultSave_ScopedData.Cache.GetOrAdd(sourceType, t =>
							{
								var tupleType = typeof(System.ValueTuple<System.Boolean, ResultSave>);
								var matchMethod = sourceType.GetMethod(nameof(Match), System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public)
									?.MakeGenericMethod(tupleType) ??
									throw new System.InvalidOperationException("Unable to locate match function on source union type. This indicates a bug in the marker detection algorithm.");
								var targetFactoryMap = typeof(UnionTypesDemo.ResultSave).GetMethods()
									.Where(c => c.CustomAttributes.Any(a => a.AttributeType.FullName == "RhoMicro.CodeAnalysis.UnionTypeFactoryAttribute"))
									.ToDictionary(c => c.GetParameters()[0].ParameterType);
								var handlers = matchMethod.GetParameters()
									.Select(p => p.ParameterType.GenericTypeArguments[0])
									.Select(t => (ParameterExpr: System.Linq.Expressions.Expression.Parameter(t), ParameterExprType: t))
									.Select(t =>
									{
										var delegateType = typeof(System.Func<,>).MakeGenericType(t.ParameterExprType, tupleType);
										System.Linq.Expressions.Expression expression = targetFactoryMap.TryGetValue(t.ParameterExprType, out var factory)
											? System.Linq.Expressions.Expression.New(tupleType.GetConstructors()[0], System.Linq.Expressions.Expression.Constant(true), System.Linq.Expressions.Expression.Call(factory, t.ParameterExpr))
											: System.Linq.Expressions.Expression.Default(tupleType);
										return System.Linq.Expressions.Expression.Lambda(delegateType, expression, t.ParameterExpr);
									}
									);var paramExpr = System.Linq.Expressions.Expression.Parameter(sourceType);
								var callExpr = System.Linq.Expressions.Expression.Call(paramExpr, matchMethod, handlers);
								var lambdaExpr = System.Linq.Expressions.Expression.Lambda(callExpr, paramExpr);
								var result = lambdaExpr.Compile();
								return result;
							}
							);
						}
						var match = (System.Func<TValue, (System.Boolean, UnionTypesDemo.ResultSave)>)weakMatch;
						var matchResult = match.Invoke(value);
						if(!matchResult.Item1)
						{
							throw new System.InvalidOperationException($"Unable to create an instance of UnionTypesDemo.ResultSave from an instance of {typeof(TValue)}.");
						}
						return matchResult.Item2;
					}
				}
			}
		}
		#endregion
		
		#region Switch
		/// <summary>
		/// Invokes a handler based on the type of value being represented.
		/// </summary>
		/// <param name="onSuccess">
		/// The handler to invoke if the union is currently representing an instance of <see cref="UnionTypesDemo.Success"/>.
		/// </param>
		/// <param name="onValidationError">
		/// The handler to invoke if the union is currently representing an instance of <see cref="UnionTypesDemo.ValidationError"/>.
		/// </param>
		public void Switch(
			System.Action<UnionTypesDemo.Success> onSuccess,
			System.Action<UnionTypesDemo.ValidationError> onValidationError)
		{
			switch(this.__tag)
			{
				case __Tag.Success:
				{
					onSuccess.Invoke(((UnionTypesDemo.Success)this.__reference!));
					return;
				}
				
				case __Tag.ValidationError:
				{
					onValidationError.Invoke(((UnionTypesDemo.ValidationError)this.__reference!));
					return;
				}
				
				default:
				{
					throw new System.InvalidOperationException("Unable to determine the represented type or value. The union type was likely not initialized correctly.");
				}
			}
		}
		#endregion
		
		#region Match
		/// <summary>
		/// Invokes a projection based on the type of value being represented.
		/// </summary>
		/// <param name="onSuccess">
		/// The projection to invoke if the union is currently representing an instance of <see cref="UnionTypesDemo.Success"/>.
		/// </param>
		/// <param name="onValidationError">
		/// The projection to invoke if the union is currently representing an instance of <see cref="UnionTypesDemo.ValidationError"/>.
		/// </param>
		/// <typeparam name="TMatchResult">
		/// The type of value produced by the projections passed.
		/// </typeparam>
		/// <returns>
		/// The projected value.
		/// </returns>
		public TMatchResult Match<TMatchResult>(
			System.Func<UnionTypesDemo.Success, TMatchResult> onSuccess,
			System.Func<UnionTypesDemo.ValidationError, TMatchResult> onValidationError) =>
		this.__tag switch
		{
			__Tag.Success => onSuccess.Invoke(((UnionTypesDemo.Success)this.__reference!))
			,
			__Tag.ValidationError => onValidationError.Invoke(((UnionTypesDemo.ValidationError)this.__reference!))
			,
			_ => throw new System.InvalidOperationException("Unable to determine the represented type or value. The union type was likely not initialized correctly.")
		}
		;
		#endregion
		
		#region Represented Type
		/// <summary>
		/// Gets the types of value this union type can represent.
		/// </summary>
		public static System.Collections.Generic.IReadOnlyCollection<System.Type> RepresentableTypes { get; } = 
			UnionTypesDemo_ResultSave_ScopedData.RepresentableTypes;
		/// <summary>
		/// Gets the type of value represented by this instance.
		/// </summary>
		public System.Type RepresentedType => 
		this.__tag switch
		{
			__Tag.Success => typeof(UnionTypesDemo.Success),
			__Tag.ValidationError => typeof(UnionTypesDemo.ValidationError),
			_ => throw new System.InvalidOperationException("Unable to determine the represented type or value. The union type was likely not initialized correctly.")
		}
		;
		#endregion
		
		#region Is/As Properties
		
		/// <summary>
		/// Gets a value indicating whether this instance is representing a value of type <see cref="UnionTypesDemo.Success"/>.
		/// </summary>
		public System.Boolean IsSuccess => __tag == __Tag.Success;
		
		/// <summary>
		/// Gets a value indicating whether this instance is representing a value of type <see cref="UnionTypesDemo.ValidationError"/>.
		/// </summary>
		public System.Boolean IsValidationError => __tag == __Tag.ValidationError;
		
		/// <summary>
		/// Retrieves the value represented by this instance as a <see cref="UnionTypesDemo.Success"/>.
		/// </summary>
		public UnionTypesDemo.Success? AsSuccess => __tag == __Tag.Success
		? ((UnionTypesDemo.Success)this.__reference!)
		: null;
		
		/// <summary>
		/// Retrieves the value represented by this instance as a <see cref="UnionTypesDemo.ValidationError"/>.
		/// </summary>
		public UnionTypesDemo.ValidationError? AsValidationError => __tag == __Tag.ValidationError
		? ((UnionTypesDemo.ValidationError)this.__reference!)
		: null;
		#endregion
		
		#region Is Group Properties
		
		#endregion
		
		#region Is/As Functions
		
		/// <summary>
		/// Determines whether this instance is representing a value of type <see cref="UnionTypesDemo.Success"/>.
		/// </summary>
		/// <returns>
		/// <see langword="true"/> if this instance is representing a value of type <see cref="UnionTypesDemo.Success"/>; otherwise, <see langword="false"/>.
		/// </returns>
		/// <param name="value">
		/// If this instance is representing a value of type <see cref="UnionTypesDemo.Success"/>, this parameter will contain that value; otherwise, <see langword="default"/>.
		/// </param>
		public System.Boolean TryAsSuccess([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out UnionTypesDemo.Success value)
		{
			if(this.__tag == __Tag.Success)
			{
				value = ((UnionTypesDemo.Success)this.__reference!);
				return true;
			}
			value = default;
			return false;
		}
		
		/// <summary>
		/// Determines whether this instance is representing a value of type <see cref="UnionTypesDemo.ValidationError"/>.
		/// </summary>
		/// <returns>
		/// <see langword="true"/> if this instance is representing a value of type <see cref="UnionTypesDemo.ValidationError"/>; otherwise, <see langword="false"/>.
		/// </returns>
		/// <param name="value">
		/// If this instance is representing a value of type <see cref="UnionTypesDemo.ValidationError"/>, this parameter will contain that value; otherwise, <see langword="default"/>.
		/// </param>
		public System.Boolean TryAsValidationError([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out UnionTypesDemo.ValidationError value)
		{
			if(this.__tag == __Tag.ValidationError)
			{
				value = ((UnionTypesDemo.ValidationError)this.__reference!);
				return true;
			}
			value = default;
			return false;
		}
		/// <summary>
		/// Determines whether this instance is representing a value of type <typeparamref name="TValue"/>.
		/// </summary>
		/// <typeparam name="TValue">
		/// The type whose representation in this instance to determine.
		/// </typeparam>
		/// <returns>
		/// <see langword="true"/> if this instance is representing a value of type <typeparamref name="TValue"/>; otherwise, <see langword="false"/>.
		/// </returns>
		public System.Boolean Is<TValue>() =>typeof(TValue) ==this.__tag switch
		{
			__Tag.Success => typeof(UnionTypesDemo.Success),
			__Tag.ValidationError => typeof(UnionTypesDemo.ValidationError),
			_ => throw new System.InvalidOperationException("Unable to determine the represented type or value. The union type was likely not initialized correctly.")
		}
		;
		/// <summary>
		/// Determines whether this instance is representing a value of type <typeparamref name="TValue"/>.
		/// </summary>
		/// <param name="value">
		/// If this instance is representing a value of type <typeparamref name="TValue"/>, this parameter will contain that value; otherwise, <see langword="default"/>.
		/// </param>
		/// <typeparam name="TValue">
		/// The type whose representation in this instance to determine.
		/// </typeparam>
		/// <returns>
		/// <see langword="true"/> if this instance is representing a value of type <typeparamref name="TValue"/>; otherwise, <see langword="false"/>.
		/// </returns>
		public System.Boolean Is<TValue>(out TValue? value)
		{
			var metadataName = RhoMicro.CodeAnalysis.UnionsGenerator.Generated.Util.GetFullString(typeof(TValue));
			switch(metadataName)
			{
				case "UnionTypesDemo.Success":
				{
					value = (RhoMicro.CodeAnalysis.UnionsGenerator.Generated.Util.UnsafeConvert<UnionTypesDemo.Success, TValue>((UnionTypesDemo.Success)this.__reference!));
					return true;
				}
				
				case "UnionTypesDemo.ValidationError":
				{
					value = (RhoMicro.CodeAnalysis.UnionsGenerator.Generated.Util.UnsafeConvert<UnionTypesDemo.ValidationError, TValue>((UnionTypesDemo.ValidationError)this.__reference!));
					return true;
				}
				default:
				{
					
					{
						value = default;
						return false;
					}
				}
			}
		}
		/// <summary>
		/// Determines whether this instance is representing an instance of <paramref name="type"/>.
		/// </summary>
		/// <param name="type">
		/// The type whose representation in this instance to determine.
		/// </param>
		/// <returns>
		/// <see langword="true"/> if this instance is representing an instance of <paramref name="type"/>; otherwise, <see langword="false"/>.
		/// </returns>
		public System.Boolean Is(System.Type type) =>
		type == this.__tag switch
		{
			__Tag.Success => typeof(UnionTypesDemo.Success),
			__Tag.ValidationError => typeof(UnionTypesDemo.ValidationError),
			_ => throw new System.InvalidOperationException("Unable to determine the represented type or value. The union type was likely not initialized correctly.")
		}
		;
		/// <summary>
		/// Retrieves the value represented by this instance as an instance of <typeparamref name="TValue"/>.
		/// </summary>
		/// <typeparam name="TValue">
		/// The type to retrieve the represented value as.
		/// </typeparam>
		/// <returns>
		/// The currently represented value as an instance of <typeparamref name="TValue"/>.
		/// </returns>
		public TValue As<TValue>() =>
		this.__tag switch
		{
			__Tag.Success => typeof(TValue) == typeof(UnionTypesDemo.Success)
			? (RhoMicro.CodeAnalysis.UnionsGenerator.Generated.Util.UnsafeConvert<UnionTypesDemo.Success, TValue>((UnionTypesDemo.Success)this.__reference!))
			: throw new System.InvalidOperationException($"Unable to convert from an instance of {typeof(UnionTypesDemo.ResultSave)} representing a value of type {this.RepresentedType} to an instance of {typeof(TValue)}."),
			__Tag.ValidationError => typeof(TValue) == typeof(UnionTypesDemo.ValidationError)
			? (RhoMicro.CodeAnalysis.UnionsGenerator.Generated.Util.UnsafeConvert<UnionTypesDemo.ValidationError, TValue>((UnionTypesDemo.ValidationError)this.__reference!))
			: throw new System.InvalidOperationException($"Unable to convert from an instance of {typeof(UnionTypesDemo.ResultSave)} representing a value of type {this.RepresentedType} to an instance of {typeof(TValue)}."),
			_ => throw new System.InvalidOperationException("Unable to determine the represented type or value. The union type was likely not initialized correctly.")
		}
		;
		#endregion
		
		#region ToString
		/// <inheritdoc/>
		public override System.String ToString()
		{
			var stringRepresentation = this.__tag switch
			{
				__Tag.Success => ((this.__reference!)?.ToString() ?? System.String.Empty),
				__Tag.ValidationError => ((this.__reference!)?.ToString() ?? System.String.Empty),
				_ => throw new System.InvalidOperationException("Unable to determine the represented type or value. The union type was likely not initialized correctly.")
			}
			;
			var result = $"ResultSave({(__tag == __Tag.Success ? "<Success>" : "Success")}|{(__tag == __Tag.ValidationError ? "<ValidationError>" : "ValidationError")}){{{stringRepresentation}}}";
			return result;
		}
		#endregion
		
		#region GetHashCode
		/// <inheritdoc/>
		public override System.Int32 GetHashCode() => 
		this.__tag switch
		{
			__Tag.Success => (System.Collections.Generic.EqualityComparer<UnionTypesDemo.Success>.Default.GetHashCode(((UnionTypesDemo.Success)this.__reference!))),
			__Tag.ValidationError => (System.Collections.Generic.EqualityComparer<UnionTypesDemo.ValidationError>.Default.GetHashCode(((UnionTypesDemo.ValidationError)this.__reference!))),
			_ => throw new System.InvalidOperationException("Unable to determine the represented type or value. The union type was likely not initialized correctly.")
		}
		;
		#endregion
		
		#region Equality
		/// <inheritdoc/>
		public override System.Boolean Equals(System.Object? obj) =>
			obj is ResultSave union && Equals(union);
		/// <inheritdoc/>
		public System.Boolean Equals(ResultSave? other) =>
			ReferenceEquals(other, this)
			|| other != null
			&& this.__tag == other.__tag
			&& this.__tag switch
			{
				__Tag.Success => (System.Collections.Generic.EqualityComparer<UnionTypesDemo.Success>.Default.Equals(((UnionTypesDemo.Success)this.__reference!), ((UnionTypesDemo.Success)other.__reference!))),
				__Tag.ValidationError => (System.Collections.Generic.EqualityComparer<UnionTypesDemo.ValidationError>.Default.Equals(((UnionTypesDemo.ValidationError)this.__reference!), ((UnionTypesDemo.ValidationError)other.__reference!))),
				_ => throw new System.InvalidOperationException("Unable to determine the represented type or value. The union type was likely not initialized correctly.")
			}
			;
		
		#endregion
		
		#region Conversions
		
		#region Representable Type Conversions
		
		/// <summary>
		/// Converts an instance of the representable type <see cref="UnionTypesDemo.Success"/> to the union type <see cref="UnionTypesDemo.ResultSave"/>.
		/// </summary>
		/// <param name="value">
		/// The value to convert.
		/// </param>
		/// <returns>
		/// The union type instance.
		/// </returns>
		public static implicit operator ResultSave(UnionTypesDemo.Success value) => CreateFromSuccess(value);
		/// <summary>
		/// Converts an instance of the union type <see cref="UnionTypesDemo.ResultSave"/> to the representable type <see cref="UnionTypesDemo.Success"/>.
		/// </summary>
		/// <param name="union">
		/// The union to convert.
		/// </param>
		/// <returns>
		/// The represented value.
		/// </returns>
		public static explicit operator UnionTypesDemo.Success(UnionTypesDemo.ResultSave union) =>union.__tag == __Tag.Success?
		((UnionTypesDemo.Success)union.__reference!):throw new System.InvalidOperationException($"Unable to convert from an instance of {typeof(ResultSave)} representing a value of type {union.RepresentedType} to an instance of {typeof(UnionTypesDemo.Success)}.");
		
		/// <summary>
		/// Converts an instance of the representable type <see cref="UnionTypesDemo.ValidationError"/> to the union type <see cref="UnionTypesDemo.ResultSave"/>.
		/// </summary>
		/// <param name="value">
		/// The value to convert.
		/// </param>
		/// <returns>
		/// The union type instance.
		/// </returns>
		public static implicit operator ResultSave(UnionTypesDemo.ValidationError value) => CreateFromValidationError(value);
		/// <summary>
		/// Converts an instance of the union type <see cref="UnionTypesDemo.ResultSave"/> to the representable type <see cref="UnionTypesDemo.ValidationError"/>.
		/// </summary>
		/// <param name="union">
		/// The union to convert.
		/// </param>
		/// <returns>
		/// The represented value.
		/// </returns>
		public static explicit operator UnionTypesDemo.ValidationError(UnionTypesDemo.ResultSave union) =>union.__tag == __Tag.ValidationError?
		((UnionTypesDemo.ValidationError)union.__reference!):throw new System.InvalidOperationException($"Unable to convert from an instance of {typeof(ResultSave)} representing a value of type {union.RepresentedType} to an instance of {typeof(UnionTypesDemo.ValidationError)}.");
		#endregion
		
		#region Related Type Conversions
		#endregion
		#endregion
	}
}
#endregion

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/UnionsGenerator

RSCG – corecraft

RSCG – corecraft
 
 

name corecraft
nuget https://www.nuget.org/packages/corecraft/
link https://github.com/AlexNav73/CoreCraft
author

Decomposing properties and class into Domain Models. Seems however too complicated to use

 

This is how you can use corecraft .

The code that you start with is


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>
	<ItemGroup>
	  <None Remove="Person.model.json" />
	</ItemGroup>
	<ItemGroup>
	  <AdditionalFiles Include="Person.model.json">
	    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
	  </AdditionalFiles>
	</ItemGroup>
	<ItemGroup>
	  <PackageReference Include="CoreCraft" Version="0.6.0" />
	  <PackageReference Include="CoreCraft.Generators" Version="0.6.0">
	    <PrivateAssets>all</PrivateAssets>
	    <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
	  </PackageReference>
	</ItemGroup>
</Project>


The code that you will use is


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>
	<ItemGroup>
	  <None Remove="Person.model.json" />
	</ItemGroup>
	<ItemGroup>
	  <AdditionalFiles Include="Person.model.json">
	    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
	  </AdditionalFiles>
	</ItemGroup>
	<ItemGroup>
	  <PackageReference Include="CoreCraft" Version="0.6.0" />
	  <PackageReference Include="CoreCraft.Generators" Version="0.6.0">
	    <PrivateAssets>all</PrivateAssets>
	    <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
	  </PackageReference>
	</ItemGroup>
</Project>



using CoreCraft;
using CoreCraft.ChangesTracking;
using CoreCraft.Subscription;
using Json2Code.Person;
using Json2Code.Person.Entities;
var model = new DomainModel(new[] { new Json2Code.Person.PersonShardModelShard() });

// Subscribe to Items collection change events 
using var subscription = model.For<IPersonShardChangesFrame>()
    .With(x => x.Persons)
    .Subscribe(OnItemChanged);

// Observe changes
void OnItemChanged(Change<ICollectionChangeSet<Person, PersonProperties>> changes)
{
    foreach (var c in changes.Hunk)
    {
        Console.WriteLine($"Entity [{c.Entity}] has been {c.Action}ed.");
        Console.WriteLine($"   Old data: {c.OldData}");
        Console.WriteLine($"   New data: {c.NewData}");
    }
}


await model.Run<IMutablePersonShardModelShard> (
    (shard, _) =>
    {
        shard.Persons.Add(new() { FirstName = "A", LastName = "B" });
        //shard.Persons.Remove(shard.Persons.First());
    });
await model.Run<IMutablePersonShardModelShard>(
    (shard, _) =>
    {        
        shard.Persons.Modify(shard.Persons.First(), p => p with { FirstName = "C" });
    });

await model.Run<IMutablePersonShardModelShard>(
    (shard, _) =>
    {
        shard.Persons.Remove(shard.Persons.First());
    });



Console.WriteLine("Press any key to exit...");
Console.ReadKey();



{
  "shards": [
    {
      "name": "PersonShard",
      "entities": [
        {
          "name": "Person",
          "properties": [
            {
              "name": "FirstName",
              "type": "string",
              "defaultValue": "\"Andrei\""
            },
            {
              "name": "LastName",
              "type": "string",
              "defaultValue": "\"Ignat\""
            }
          ]
        }
      ],
      "collections": [
        {
          "name": "Persons",
          "entityType": "Person"
        }
      ],
      "relations": []
    }
  ]
}

 

The code that is generated is


//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by the tool.
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

#nullable enable

namespace Json2Code.Person
{
    using CoreCraft.Core;
    using CoreCraft.ChangesTracking;
    using CoreCraft.Persistence;
    using Json2Code.Person.Entities;

    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("C# Source Generator", "1.0.0.0")]
    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    public interface IPersonShardModelShard : IModelShard
    {
        ICollection<Person, PersonProperties> Persons { get; }

    }

    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("C# Source Generator", "1.0.0.0")]
    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    public interface IMutablePersonShardModelShard : IModelShard
    {
        IMutableCollection<Person, PersonProperties> Persons { get; }

    }

    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("C# Source Generator", "1.0.0.0")]
    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
    [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute()]
    internal static class PersonShardModelShardInfo
    {
        public static readonly CollectionInfo PersonsInfo = new("PersonShard", "Persons", new PropertyInfo[] { new("FirstName", typeof(string), false), new("LastName", typeof(string), false) });

    }

    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("C# Source Generator", "1.0.0.0")]
    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
    [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute()]
    internal sealed partial class PersonShardModelShard : IPersonShardModelShard
    {
        public const string PersonsId = "Json2Code.Person.PersonShard.Persons";

        public PersonShardModelShard()
        {
            Persons = new Collection<Person, PersonProperties>(
                PersonsId,
                static id => new Person(id),
                static () => new PersonProperties());

        }

        internal PersonShardModelShard(IMutablePersonShardModelShard mutable)
        {
            Persons = ((IMutableState<ICollection<Person, PersonProperties>>)mutable.Persons).AsReadOnly();

        }

        public ICollection<Person, PersonProperties> Persons { get; init; } = null!;

    }

    internal sealed partial class PersonShardModelShard : IReadOnlyState<IMutablePersonShardModelShard>
    {
        public IMutablePersonShardModelShard AsMutable(global::System.Collections.Generic.IEnumerable<IFeature> features)
        {
            var persons = (IMutableCollection<Person, PersonProperties>)Persons;


            foreach (var feature in features)
            {
                persons = feature.Decorate(this, persons);

            }

            return new MutablePersonShardModelShard()
            {
                Persons = persons,

            };
        }
    }

    internal sealed partial class PersonShardModelShard : ICanBeSaved
    {
        public void Save(IRepository repository)
        {
            repository.Save(PersonShardModelShardInfo.PersonsInfo, Persons);

        }
    }

    internal sealed partial class PersonShardModelShard : IFeatureContext
    {
        IChangesFrame IFeatureContext.GetOrAddFrame(IMutableModelChanges modelChanges)
        {
            return modelChanges.Register(static () => new PersonShardChangesFrame());
        }
    }

    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("C# Source Generator", "1.0.0.0")]
    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    public interface IPersonShardChangesFrame : IChangesFrame
    {
        ICollectionChangeSet<Person, PersonProperties> Persons { get; }

    }

    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("C# Source Generator", "1.0.0.0")]
    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
    [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute()]
    internal sealed class PersonShardChangesFrame : IPersonShardChangesFrame, IChangesFrameEx, ICanBeSaved
    {
        public PersonShardChangesFrame()
        {
            Persons = new CollectionChangeSet<Person, PersonProperties>(PersonShardModelShard.PersonsId);

        }

        public ICollectionChangeSet<Person, PersonProperties> Persons { get; private set; }


        ICollectionChangeSet<TEntity, TProperty>? IChangesFrame.Get<TEntity, TProperty>(ICollection<TEntity, TProperty> collection)
        {
            if (Persons.Id == collection.Id) return Persons as ICollectionChangeSet<TEntity, TProperty>;

            throw new System.InvalidOperationException("Unable to find collection's changes set");
        }

        IRelationChangeSet<TParent, TChild>? IChangesFrame.Get<TParent, TChild>(IRelation<TParent, TChild> relation)
        {

            throw new System.InvalidOperationException($"Unable to find relation's change set");
        }

        IChangesFrame IChangesFrame.Invert()
        {
            return new PersonShardChangesFrame()
            {
                Persons = Persons.Invert(),

            };
        }

        public void Apply(IModel model)
        {
            var modelShard = model.Shard<IMutablePersonShardModelShard>();

            Persons.Apply(modelShard.Persons);
        }

        public bool HasChanges()
        {
            return Persons.HasChanges();
        }

        public IChangesFrame Merge(IChangesFrame frame)
        {
            var typedFrame = (PersonShardChangesFrame)frame;

            return new PersonShardChangesFrame()
            {
                Persons = Persons.Merge(typedFrame.Persons),

            };
        }

        public void Save(IRepository repository)
        {
            repository.Save(PersonShardModelShardInfo.PersonsInfo, Persons);

        }

    }

    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("C# Source Generator", "1.0.0.0")]
    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
    [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute()]
    internal sealed class MutablePersonShardModelShard : IMutablePersonShardModelShard, IMutableState<IPersonShardModelShard>, ICanBeLoaded
    {
        public IMutableCollection<Person, PersonProperties> Persons { get; init; } = null!;


        public IPersonShardModelShard AsReadOnly()
        {
            return new PersonShardModelShard(this);
        }

        public void Load(IRepository repository)
        {
            repository.Load(PersonShardModelShardInfo.PersonsInfo, Persons);

        }
    }

}

namespace Json2Code.Person.Entities
{
    using CoreCraft.Core;

    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("C# Source Generator", "1.0.0.0")]
    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
    [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute()]
    public sealed record Person(global::System.Guid Id) : Entity(Id)
    {
        internal Person() : this(global::System.Guid.NewGuid())
        {
        }
    }

    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("C# Source Generator", "1.0.0.0")]
    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
    [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute()]
    public sealed partial record PersonProperties : Properties
    {
        public PersonProperties()
        {
            FirstName = "Andrei";
            LastName = "Ignat";
        }

        public string FirstName { get; init; }
        public string LastName { get; init; }

#if NET5_0_OR_GREATER
        public override PersonProperties ReadFrom(IPropertiesBag bag)
#else
        public override Properties ReadFrom(IPropertiesBag bag)
#endif
        {
            return new PersonProperties()
            {
                FirstName = bag.Read<string>("FirstName"),
                LastName = bag.Read<string>("LastName"),
            };
        }

        public override void WriteTo(IPropertiesBag bag)
        {
            bag.Write("FirstName", FirstName);
            bag.Write("LastName", LastName);
        }

    }


}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/corecraft

RSCG – sourcedepend

RSCG – sourcedepend
 
 

name sourcedepend
nuget https://www.nuget.org/packages/sourcedepend/
link https://github.com/crwsolutions/sourcedepend
author Colin Wilmans

Generating constructor for DI

 

This is how you can use sourcedepend .

The code that you start with is


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>

	<ItemGroup>
	  <PackageReference Include="SourceDepend" Version="0.3.0" />
	</ItemGroup>

</Project>


The code that you will use is


using CtorDemo;

var p = new Person("Andrei", "Ignat");

Console.WriteLine(p.FullName());



namespace CtorDemo;
internal partial class Person
{
    [Dependency]
    public readonly string? FirstName;
    [Dependency]
    public readonly string? LastName;
    
    public string FullName() => $"{FirstName} {LastName}";

    partial void PostConstruct()
    {
        Console.WriteLine("Person constructed");
    }
    partial void PreConstruct()
    {
        Console.WriteLine("Person constructing");
    }

}


 

The code that is generated is

// <auto-generated />

/// <summary>
/// Injects this item in the constructor. This will also highjack your constructor, so if you have any construct business, use PreConstruct() or PostConstruct() methods.
/// </summary>
/// <remarks>
/// Make sure your class is partial.
/// </remarks>
[System.AttributeUsage(System.AttributeTargets.Field | System.AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
[System.Diagnostics.Conditional("DependencyGenerator_DEBUG")]
internal sealed class DependencyAttribute : System.Attribute
{
    internal DependencyAttribute(string alternativePropertyName = null) { }
}

// <auto-generated/>
#pragma warning disable
#nullable enable
namespace CtorDemo
{
    /// <inheritdoc/>
    internal partial class Person
    {
        public Person(string? FirstName, string? LastName)
        {
            PreConstruct();

            this.FirstName = FirstName;
            this.LastName = LastName;

            PostConstruct();
        }

        partial void PreConstruct();
        partial void PostConstruct();
    }
}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/sourcedepend

RSCG – OptionToStringGenerator

RSCG – OptionToStringGenerator
 
 

name OptionToStringGenerator
nuget https://www.nuget.org/packages/Seekatar.OptionToStringGenerator/
link https://github.com/Seekatar/OptionToStringGenerator
author Jim W

Generating similar ToString method for classes with many properties. It can also generate for external classes.

 

This is how you can use OptionToStringGenerator .

The code that you start with is


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Seekatar.OptionToStringGenerator" Version="0.3.1" />
  </ItemGroup>
	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>
</Project>


The code that you will use is


using Class2String;
using Seekatar.OptionToStringGenerator;
var p = new Person();
p.FirstName = "Andrei";
p.LastName = "Ignat";
p.Age = 50;
Console.WriteLine(p.OptionsToString());    


using Seekatar.OptionToStringGenerator;

namespace Class2String;

[OptionsToString]
internal class Person
{
    [OutputMask(PrefixLen = 3)]
    public string? FirstName { get; set; }
    [OutputMask(SuffixLen = 3)]
    public string? LastName { get; set; }

    public string FUllName => $"{FirstName} {LastName}";

    [OutputIgnore]
    public int Age { get; set; }
}

 

The code that is generated is

#nullable enable
using static Seekatar.Mask;
namespace Seekatar.OptionToStringGenerator
{
    public static partial class ClassExtensions
    {
        internal static string OptionsToString(this Class2String.Person o)
        {
            return $@"Class2String.Person:
  FirstName : {Format(o?.FirstName,prefixLen:3,suffixLen:0)}
  LastName  : {Format(o?.LastName,prefixLen:0,suffixLen:3)}
  FUllName  : {Format(o?.FUllName)}
";
        }
    }
}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/OptionToStringGenerator

RSCG – cachesourcegenerator

RSCG – cachesourcegenerator
 
 

name cachesourcegenerator
nuget https://www.nuget.org/packages/cachesourcegenerator/
link https://github.com/jeppevammenkristensen/cachesourcegenerator
author Jeppe Roi Kristensen

Caching methods results

 

This is how you can use cachesourcegenerator .

The code that you start with is


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="CacheSourceGenerator" Version="0.4.1" />
    <PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0" />
  </ItemGroup>
	<ItemGroup>
		<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" />
	</ItemGroup>
 <PropertyGroup>
        <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
        <CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
    </PropertyGroup>
</Project>


The code that you will use is


using CacheDemo;
using Microsoft.Extensions.Caching.Memory;
var f=new FibTest(new MemoryCache(new MemoryCacheOptions()));
Console.WriteLine(f.FibMemo(5));
Console.WriteLine("and now with cache hit:");
Console.WriteLine(f.FibMemo(5));



using Microsoft.Extensions.Caching.Memory;
using CacheSourceGenerator;
namespace CacheDemo;
internal partial class FibTest
{
    private readonly IMemoryCache _memoryCache;

    public FibTest(IMemoryCache memoryCache)
    {
        _memoryCache = memoryCache;
    }
    void ProcessCacheEntry(ICacheEntry entry)
    {
        entry.SlidingExpiration = TimeSpan.FromMinutes(2);
    }

    [GenerateMemoryCache(MethodName = "FibMemo", CacheEnricherProcessor = nameof(ProcessCacheEntry))]
    public int Fib(int n)
    {
        if (n <= 1)
        {
            return n;
        }
        Console.WriteLine($"Calculating Fib({n})");
        //return Fib(n - 1) + Fib(n - 2);
        return FibMemo(n - 1) + FibMemo(n - 2);
    }
}

 

The code that is generated is

#nullable enable
//autogenerated
using Microsoft.Extensions.Caching.Memory;
using CacheSourceGenerator;
using System;

namespace CacheDemo;
internal partial class FibTest
{
    public int FibMemo(int n)
    {
        var _key_ = new
        {
            _MethodName = "Fib",
            _ClassName = "FibTest",
            n
        };
        IMemoryCache _cache_ = _memoryCache;
        var _result_ = _cache_.GetOrCreate(_key_, _entry_ =>
        {
            ProcessCacheEntry(_entry_);
            OnCallingFib(n);
            var _callResult_ = Fib(n);
            OnCalledFib(n, _callResult_);
            ;
            return _callResult_;
        });
        return _result_;
    }

    public void FibMemo_Evict(int n)
    {
        var _key_ = new
        {
            _MethodName = "Fib",
            _ClassName = "FibTest",
            n
        };
        IMemoryCache _cache_ = _memoryCache;
        _cache_.Remove(_key_);
    }

    partial void OnCallingFib(int n);
    partial void OnCalledFib(int n, int _returned_);
}

// <auto-generated/>
#nullable enable
namespace CacheSourceGenerator
{
    [System.AttributeUsage(System.AttributeTargets.Method)]
    public class GenerateMemoryCacheAttribute : System.Attribute
    {
       /// <summary>
       /// The name of the generated cache method 
       /// </summary>
       public string MethodName { get;init; } = default!;
       
       /// <summary>
       /// The name of a method in the current class that takes
       /// an CacheEntry and processes it 
       /// </summary>
       public string? CacheEnricherProcessor { get;set; }
       
       
       /// <summary>
        /// The name of a method in the current class that can 
        /// generate a custom cache key. The method must take the same parameters
        /// as the method being decorated. But can return any type.
        /// </summary>
       public string? KeyGenerator {get;set;}
       
       /// <summary>
       /// Set this to true to not generate an evict method
       /// </summary>
       public bool SuppressEvictMethod {get;set;}
    }
}
// <auto-generated/>
#nullable enable
namespace CacheSourceGenerator
{
    [System.AttributeUsage(System.AttributeTargets.Parameter)]
    public class IgnoreKeyAttribute : System.Attribute
    {
        
    }
}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/cachesourcegenerator

RSCG – jab

RSCG – jab
 
 

name jab
nuget https://www.nuget.org/packages/jab/
link https://github.com/pakrym/jab
author Pavel Krymets

generating DI code

 

This is how you can use jab .

The code that you start with is


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
	<ItemGroup>
		<PackageReference Include="Jab" Version="0.10.2" PrivateAssets="all" />
	</ItemGroup>
	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>
</Project>


The code that you will use is


using InjectDemo;
using Jab;
MyServiceProvider sc = new();
//var con = sc.GetService<DatabaseCon>();
var db = sc.GetService<IDatabase>();
db.Open();


[ServiceProvider]
//[Transient(typeof(DatabaseCon), typeof(DatabaseCon))]
[Transient(typeof(IDatabase), typeof(DatabaseCon))]
internal partial class MyServiceProvider { }


namespace InjectDemo;

internal class Database : IDatabase
{
    private readonly DatabaseCon con;

    public Database(DatabaseCon con)
    {
        this.con = con;
    }
    public void Open()
    {
        Console.WriteLine($"open {con.Connection}");
    }

}





namespace InjectDemo;

internal class DatabaseCon: IDatabase
{
    public string? Connection { get; set; }
    public void Open()
    {
        Console.WriteLine("open" + Connection);
    }
}



 

The code that is generated is

// <auto-generated/>

#if !JAB_ATTRIBUTES_REFERENCED || JAB_ATTRIBUTES_PACKAGE

using System;
using System.Threading.Tasks;

#nullable enable

namespace Jab
{
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
#if JAB_ATTRIBUTES_PACKAGE
    public
#else
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Jab", null)]
    internal
#endif
    class ServiceProviderAttribute: Attribute
    {
        public Type[]? RootServices { get; set; }
    }

    [AttributeUsage(AttributeTargets.Interface, AllowMultiple = false, Inherited = true)]
#if JAB_ATTRIBUTES_PACKAGE
    public
#else
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Jab", null)]
    internal
#endif
    class ServiceProviderModuleAttribute: Attribute
    {
    }

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true, Inherited = true)]
#if JAB_ATTRIBUTES_PACKAGE
    public
#else
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Jab", null)]
    internal
#endif
    class ImportAttribute: Attribute
    {
        public Type ModuleType { get; }

        public ImportAttribute(Type moduleType)
        {
            ModuleType = moduleType;
        }
    }

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true, Inherited = true)]
#if JAB_ATTRIBUTES_PACKAGE
    public
#else
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Jab", null)]
    internal
#endif
    class SingletonAttribute: Attribute
    {
        public Type ServiceType { get; }

        public string? Name { get; set; }

        public Type? ImplementationType { get; }

        public string? Instance { get; set; }

        public string? Factory { get; set; }

        public SingletonAttribute(Type serviceType)
        {
            ServiceType = serviceType;
        }

        public SingletonAttribute(Type serviceType, Type implementationType)
        {
            ServiceType = serviceType;
            ImplementationType = implementationType;
        }
    }

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true, Inherited = true)]
#if JAB_ATTRIBUTES_PACKAGE
    public
#else
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Jab", null)]
    internal
#endif
    class TransientAttribute : Attribute
    {
        public Type ServiceType { get; }
        public string? Name { get; set; }

        public Type? ImplementationType { get; }

        public string? Factory { get; set; }

        public TransientAttribute(Type serviceType)
        {
            ServiceType = serviceType;
        }

        public TransientAttribute(Type serviceType, Type implementationType)
        {
            ServiceType = serviceType;
            ImplementationType = implementationType;
        }
    }

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true, Inherited = true)]
#if JAB_ATTRIBUTES_PACKAGE
    public
#else
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Jab", null)]
    internal
#endif
    class ScopedAttribute : Attribute
    {
        public Type ServiceType { get; }
        public string? Name { get; set; }

        public Type? ImplementationType { get; }

        public string? Factory { get; set; }

        public ScopedAttribute(Type serviceType)
        {
            ServiceType = serviceType;
        }

        public ScopedAttribute(Type serviceType, Type implementationType)
        {
            ServiceType = serviceType;
            ImplementationType = implementationType;
        }
    }


    [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
#if JAB_ATTRIBUTES_PACKAGE
    public
#else
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Jab", null)]
    internal
#endif
        class FromNamedServicesAttribute : Attribute
    {
        public string? Name { get; set; }

        public FromNamedServicesAttribute(string name)
        {
            Name = name;
        }
    }

#if GENERIC_ATTRIBUTES
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true, Inherited = true)]
#if JAB_ATTRIBUTES_PACKAGE
    public
#else
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Jab", null)]
    internal
#endif
    class ImportAttribute<TModule> : ImportAttribute
    {
        public ImportAttribute() : base(typeof(TModule))
        {
        }
    }

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true, Inherited = true)]
#if JAB_ATTRIBUTES_PACKAGE
    public
#else
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Jab", null)]
    internal
#endif
    class TransientAttribute<TService> : TransientAttribute
    {
        public TransientAttribute() : base(typeof(TService))
        {
        }
    }

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true, Inherited = true)]
#if JAB_ATTRIBUTES_PACKAGE
    public
#else
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Jab", null)]
    internal
#endif
    class TransientAttribute<TService, TImpl> : TransientAttribute where TImpl: TService
    {
        public TransientAttribute() : base(typeof(TService), typeof(TImpl))
        {
        }
    }

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true, Inherited = true)]
#if JAB_ATTRIBUTES_PACKAGE
    public
#else
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Jab", null)]
    internal
#endif
    class ScopedAttribute<TService> : ScopedAttribute
    {
        public ScopedAttribute() : base(typeof(TService))
        {
        }
    }

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true, Inherited = true)]
#if JAB_ATTRIBUTES_PACKAGE
    public
#else
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Jab", null)]
    internal
#endif
    class ScopedAttribute<TService, TImpl> : ScopedAttribute where TImpl: TService
    {
        public ScopedAttribute() : base(typeof(TService), typeof(TImpl))
        {
        }
    }


    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true, Inherited = true)]
#if JAB_ATTRIBUTES_PACKAGE
    public
#else
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Jab", null)]
    internal
#endif
    class SingletonAttribute<TService> : SingletonAttribute
    {
        public SingletonAttribute() : base(typeof(TService))
        {
        }
    }

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true, Inherited = true)]
#if JAB_ATTRIBUTES_PACKAGE
    public
#else
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Jab", null)]
    internal
#endif
    class SingletonAttribute<TService, TImpl> : SingletonAttribute where TImpl: TService
    {
        public SingletonAttribute() : base(typeof(TService), typeof(TImpl))
        {
        }
    }

#endif

#if JAB_ATTRIBUTES_PACKAGE
    public
#else
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Jab", null)]
    internal
#endif
    interface IServiceProvider<T>
    {
        T GetService();
    }

#if JAB_ATTRIBUTES_PACKAGE
    public
#else
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Jab", null)]
    internal
#endif
    interface INamedServiceProvider<T>
    {
        T GetService(string name);
    }

#if JAB_ATTRIBUTES_PACKAGE
    public
#else
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Jab", null)]
    internal
#endif
    static class JabHelpers
    {
        public static InvalidOperationException CreateServiceNotFoundException<T>(string? name = null) =>
            CreateServiceNotFoundException(typeof(T), name);
        public static InvalidOperationException CreateServiceNotFoundException(Type type, string? name = null) =>
            new InvalidOperationException(
                name != null ?
                    $"Service with type {type} and name {name} not registered" :
                    $"Service with type {type} not registered");
    }
}

#endif

// <auto-generated/>

#nullable enable

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using Jab;

using static Jab.JabHelpers;
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Jab", "0.10.2.0")]
internal partial class MyServiceProvider : global::System.IDisposable,
   System.IAsyncDisposable,
   global::System.IServiceProvider,
   IServiceProvider<InjectDemo.DatabaseCon>,
   IServiceProvider<InjectDemo.IDatabase>,
   IServiceProvider<System.IServiceProvider>
{
    private Scope? _rootScope;
    
    InjectDemo.DatabaseCon IServiceProvider<InjectDemo.DatabaseCon>.GetService()
    {
        InjectDemo.DatabaseCon service = new InjectDemo.DatabaseCon();
        TryAddDisposable(service);
        return service;
    }
    
    InjectDemo.IDatabase IServiceProvider<InjectDemo.IDatabase>.GetService()
    {
        InjectDemo.DatabaseCon service = new InjectDemo.DatabaseCon();
        TryAddDisposable(service);
        return service;
    }
    
    System.IServiceProvider IServiceProvider<System.IServiceProvider>.GetService()
    {
        return this;
    }
    
    object? global::System.IServiceProvider.GetService(global::System.Type type){
        if (type == typeof(InjectDemo.DatabaseCon)) return this.GetService<InjectDemo.DatabaseCon>();
        if (type == typeof(InjectDemo.IDatabase)) return this.GetService<InjectDemo.IDatabase>();
        if (type == typeof(System.IServiceProvider)) return this.GetService<System.IServiceProvider>();
        return null;
    }
    
    private global::System.Collections.Generic.List<object>? _disposables;
    
    private void TryAddDisposable(object? value){
        if (value is global::System.IDisposable || value is System.IAsyncDisposable)
        lock (this){
            (_disposables ??= new global::System.Collections.Generic.List<object>()).Add(value);
        }
    }
    
    public void Dispose(){
        void TryDispose(object? value) => (value as IDisposable)?.Dispose();
        
        TryDispose(_rootScope);
        if (_disposables != null){
            foreach (var service in _disposables){
                TryDispose(service);
            }
        }
    }
    
    public async global::System.Threading.Tasks.ValueTask DisposeAsync(){
        global::System.Threading.Tasks.ValueTask TryDispose(object? value){
            if (value is System.IAsyncDisposable asyncDisposable){
                return asyncDisposable.DisposeAsync();
            }
            else if (value is global::System.IDisposable disposable){
                disposable.Dispose();
            }
            return default;
        }
        
        await TryDispose(_rootScope);
        if (_disposables != null){
            foreach (var service in _disposables){
                await TryDispose(service);
            }
        }
    }
    
    [DebuggerHidden]
    public T GetService<T>() => this is IServiceProvider<T> provider ? provider.GetService() : throw CreateServiceNotFoundException<T>();
    
    [DebuggerHidden]
    public T GetService<T>(string name) => this is INamedServiceProvider<T> provider ? provider.GetService(name) : throw CreateServiceNotFoundException<T>(name);
    
    public Scope CreateScope() => new Scope(this);
    
    public partial class Scope : global::System.IDisposable,
       System.IAsyncDisposable,
       global::System.IServiceProvider,
       IServiceProvider<InjectDemo.DatabaseCon>,
       IServiceProvider<InjectDemo.IDatabase>,
       IServiceProvider<System.IServiceProvider>    
    {
        
        private MyServiceProvider _root;
        
        public Scope(MyServiceProvider root){
            _root = root;
        }
        
        [DebuggerHidden]
        public T GetService<T>() => this is IServiceProvider<T> provider ? provider.GetService() : throw CreateServiceNotFoundException<T>();
        
        [DebuggerHidden]
        public T GetService<T>(string name) => this is INamedServiceProvider<T> provider ? provider.GetService(name) : throw CreateServiceNotFoundException<T>(name);
        
        InjectDemo.DatabaseCon IServiceProvider<InjectDemo.DatabaseCon>.GetService(){
            InjectDemo.DatabaseCon service = new InjectDemo.DatabaseCon();
            TryAddDisposable(service);
            return service;
        }
        
        InjectDemo.IDatabase IServiceProvider<InjectDemo.IDatabase>.GetService(){
            InjectDemo.DatabaseCon service = new InjectDemo.DatabaseCon();
            TryAddDisposable(service);
            return service;
        }
        
        System.IServiceProvider IServiceProvider<System.IServiceProvider>.GetService(){
            return this;
        }
        
        object? global::System.IServiceProvider.GetService(global::System.Type type){
            if (type == typeof(InjectDemo.DatabaseCon)) return this.GetService<InjectDemo.DatabaseCon>();
            if (type == typeof(InjectDemo.IDatabase)) return this.GetService<InjectDemo.IDatabase>();
            if (type == typeof(System.IServiceProvider)) return this.GetService<System.IServiceProvider>();
            return null;
        }
        
        private global::System.Collections.Generic.List<object>? _disposables;
        
        private void TryAddDisposable(object? value){
            if (value is global::System.IDisposable || value is System.IAsyncDisposable)
            lock (this){
                (_disposables ??= new global::System.Collections.Generic.List<object>()).Add(value);
            }
        }
        
        public void Dispose(){
            void TryDispose(object? value) => (value as IDisposable)?.Dispose();
            
            if (_disposables != null){
                foreach (var service in _disposables){
                    TryDispose(service);
                }
            }
        }
        
        public async global::System.Threading.Tasks.ValueTask DisposeAsync(){
            global::System.Threading.Tasks.ValueTask TryDispose(object? value){
                if (value is System.IAsyncDisposable asyncDisposable){
                    return asyncDisposable.DisposeAsync();
                }
                else if (value is global::System.IDisposable disposable){
                    disposable.Dispose();
                }
                return default;
            }
            
            if (_disposables != null){
                foreach (var service in _disposables){
                    await TryDispose(service);
                }
            }
        }
        
    }
    private Scope GetRootScope(){
        if (_rootScope == default)
        lock (this)
        if (_rootScope == default){
            _rootScope = CreateScope();
        }
        return _rootScope;
    }
}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/jab

RSCG – FunicularSwitch

RSCG – FunicularSwitch
 
 

name FunicularSwitch
nuget https://www.nuget.org/packages/FunicularSwitch.Generators/
https://www.nuget.org/packages/FunicularSwitch
link https://github.com/bluehands/Funicular-Switch
author bluehands

Generating discriminated unions for C# 9.0 and above.

 

This is how you can use FunicularSwitch .

The code that you start with is


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

 
	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>

 
	<ItemGroup>
	  <PackageReference Include="FunicularSwitch" Version="5.0.1" />
	  <PackageReference Include="FunicularSwitch.Generators" Version="3.2.0">
	    <PrivateAssets>all</PrivateAssets>
	    <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
	  </PackageReference>
	</ItemGroup>
</Project>


The code that you will use is


using Union;

Console.WriteLine("Save or not");
var data = SaveToDatabase.Save(0);

Console.WriteLine(data.Match(
    ok => true,
    error => false));
data = SaveToDatabase.Save(1);
Console.WriteLine(data.Match(ok => true, error => false));



namespace Union;

[FunicularSwitch.Generators.ResultType(ErrorType = typeof(ErrorDetails))]
public abstract partial class ResultSave<T> { };

public class ErrorDetails
{
    
}



    //[FunicularSwitch.Generators.UnionType]
    //public abstract partial class ResultSave { };

    //public sealed partial record Success(int Value): ResultSave;
    //public sealed partial record ValidationError(string Message):ResultSave;

    ////public sealed partial record Ok(T Value) : ResultSave<T>;

    ////public sealed partial record Error(Exception Exception) : ResultSave<T>;



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Union;
internal class SaveToDatabase
{
    public static ResultSave<int> Save(int i)
    {
        if (i == 0)
        {
            return new ResultSave<int>.Error_(new ErrorDetails());
        }
        return new ResultSave<int>.Ok_(i);
    }
}


 

The code that is generated is

using System;

// ReSharper disable once CheckNamespace
namespace FunicularSwitch.Generators
{
    [AttributeUsage(AttributeTargets.Enum)]
    sealed class ExtendedEnumAttribute : Attribute
    {
	    public EnumCaseOrder CaseOrder { get; set; } = EnumCaseOrder.AsDeclared;
	    public ExtensionAccessibility Accessibility { get; set; } = ExtensionAccessibility.Public;
    }
    
    enum EnumCaseOrder
    {
        Alphabetic,
        AsDeclared
    }

    /// <summary>
    /// Generate match methods for all enums defined in assembly that contains AssemblySpecifier.
    /// </summary>
    [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
    class ExtendEnumsAttribute : Attribute
    {
	    public Type AssemblySpecifier { get; }
	    public EnumCaseOrder CaseOrder { get; set; } = EnumCaseOrder.AsDeclared;
	    public ExtensionAccessibility Accessibility { get; set; } = ExtensionAccessibility.Public;

	    public ExtendEnumsAttribute() => AssemblySpecifier = typeof(ExtendEnumsAttribute);

	    public ExtendEnumsAttribute(Type assemblySpecifier)
	    {
		    AssemblySpecifier = assemblySpecifier;
	    }
    }

    /// <summary>
    /// Generate match methods for Type. Must be enum.
    /// </summary>
    [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
    class ExtendEnumAttribute : Attribute
    {
	    public Type Type { get; }

	    public EnumCaseOrder CaseOrder { get; set; } = EnumCaseOrder.AsDeclared;

	    public ExtensionAccessibility Accessibility { get; set; } = ExtensionAccessibility.Public;

	    public ExtendEnumAttribute(Type type)
	    {
		    Type = type;
	    }
    }

    enum ExtensionAccessibility
    {
	    Internal,
	    Public
    }
}
using System;

// ReSharper disable once CheckNamespace
namespace FunicularSwitch.Generators
{
	/// <summary>
	/// Mark an abstract partial type with a single generic argument with the ResultType attribute.
	/// This type from now on has Ok | Error semantics with map and bind operations.
	/// </summary>
    [AttributeUsage(AttributeTargets.Class, Inherited = false)]
    sealed class ResultTypeAttribute : Attribute
    {
        public ResultTypeAttribute() => ErrorType = typeof(string);
        public ResultTypeAttribute(Type errorType) => ErrorType = errorType;

        public Type ErrorType { get; set; }
    }

    /// <summary>
    /// Mark a static method or a member method or you error type with the MergeErrorAttribute attribute.
    /// Static signature: TError -> TError -> TError. Member signature: TError -> TError
    /// We are now able to collect errors and methods like Validate, Aggregate, FirstOk that are useful to combine results are generated.
    /// </summary>
    [AttributeUsage(AttributeTargets.Method, Inherited = false)]
    sealed class MergeErrorAttribute : Attribute
    {
    }

    /// <summary>
    /// Mark a static method with the ExceptionToError attribute.
    /// Signature: Exception -> TError
    /// This method is always called, when an exception happens in a bind operation.
    /// So a call like result.Map(i => i/0) will return an Error produced by the factory method instead of throwing the DivisionByZero exception.
    /// </summary>
    [AttributeUsage(AttributeTargets.Method, Inherited = false)]
    sealed class ExceptionToError : Attribute
    {
    }
}
#nullable enable
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using FunicularSwitch;

namespace Union
{
#pragma warning disable 1591
    public abstract partial class ResultSave
    {
        public static ResultSave<T> Error<T>(ErrorDetails details) => new ResultSave<T>.Error_(details);
        public static ResultSave<T> Ok<T>(T value) => new ResultSave<T>.Ok_(value);
        public bool IsError => GetType().GetGenericTypeDefinition() == typeof(ResultSave<>.Error_);
        public bool IsOk => !IsError;
        public abstract ErrorDetails? GetErrorOrDefault();

        public static ResultSave<T> Try<T>(Func<T> action, Func<Exception, ErrorDetails> formatError)
        {
            try
            {
                return action();
            }
            catch (Exception e)
            {
                return Error<T>(formatError(e));
            }
        }

        public static async Task<ResultSave<T>> Try<T>(Func<Task<T>> action, Func<Exception, ErrorDetails> formatError)
        {
            try
            {
                return await action();
            }
            catch (Exception e)
            {
                return Error<T>(formatError(e));
            }
        }
    }

    public abstract partial class ResultSave<T> : ResultSave, IEnumerable<T>
    {
        public static ResultSave<T> Error(ErrorDetails message) => Error<T>(message);
        public static ResultSave<T> Ok(T value) => Ok<T>(value);

        public static implicit operator ResultSave<T>(T value) => ResultSave.Ok(value);

        public static bool operator true(ResultSave<T> result) => result.IsOk;
        public static bool operator false(ResultSave<T> result) => result.IsError;

        public static bool operator !(ResultSave<T> result) => result.IsError;

        //just here to suppress warning, never called because all subtypes (Ok_, Error_) implement Equals and GetHashCode
        bool Equals(ResultSave<T> other) => this switch
        {
            Ok_ ok => ok.Equals((object)other),
            Error_ error => error.Equals((object)other),
            _ => throw new InvalidOperationException($"Unexpected type derived from {nameof(ResultSave<T>)}")
        };

        public override int GetHashCode() => this switch
        {
            Ok_ ok => ok.GetHashCode(),
            Error_ error => error.GetHashCode(),
            _ => throw new InvalidOperationException($"Unexpected type derived from {nameof(ResultSave<T>)}")
        };

        public override bool Equals(object? obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            if (obj.GetType() != this.GetType()) return false;
            return Equals((ResultSave<T>)obj);
        }

        public static bool operator ==(ResultSave<T>? left, ResultSave<T>? right) => Equals(left, right);

        public static bool operator !=(ResultSave<T>? left, ResultSave<T>? right) => !Equals(left, right);

        public void Match(Action<T> ok, Action<ErrorDetails>? error = null) => Match(
            v =>
            {
                ok.Invoke(v);
                return 42;
            },
            err =>
            {
                error?.Invoke(err);
                return 42;
            });

        public T1 Match<T1>(Func<T, T1> ok, Func<ErrorDetails, T1> error)
        {
            return this switch
            {
                Ok_ okResultSave => ok(okResultSave.Value),
                Error_ errorResultSave => error(errorResultSave.Details),
                _ => throw new InvalidOperationException($"Unexpected derived result type: {GetType()}")
            };
        }

        public async Task<T1> Match<T1>(Func<T, Task<T1>> ok, Func<ErrorDetails, Task<T1>> error)
        {
            return this switch
            {
                Ok_ okResultSave => await ok(okResultSave.Value).ConfigureAwait(false),
                Error_ errorResultSave => await error(errorResultSave.Details).ConfigureAwait(false),
                _ => throw new InvalidOperationException($"Unexpected derived result type: {GetType()}")
            };
        }

        public Task<T1> Match<T1>(Func<T, Task<T1>> ok, Func<ErrorDetails, T1> error) =>
            Match(ok, e => Task.FromResult(error(e)));

        public async Task Match(Func<T, Task> ok)
        {
            if (this is Ok_ okResultSave) await ok(okResultSave.Value).ConfigureAwait(false);
        }

        public T Match(Func<ErrorDetails, T> error) => Match(v => v, error);

        public ResultSave<T1> Bind<T1>(Func<T, ResultSave<T1>> bind)
        {
            switch (this)
            {
                case Ok_ ok:
	                try
	                {
		                return bind(ok.Value);
	                }
	                // ReSharper disable once RedundantCatchClause
#pragma warning disable CS0168 // Variable is declared but never used
	                catch (Exception e)
#pragma warning restore CS0168 // Variable is declared but never used
	                {
		                throw; //createGenericErrorResult
	                }
                case Error_ error:
                    return error.Convert<T1>();
                default:
                    throw new InvalidOperationException($"Unexpected derived result type: {GetType()}");
            }
        }

        public async Task<ResultSave<T1>> Bind<T1>(Func<T, Task<ResultSave<T1>>> bind)
        {
            switch (this)
            {
                case Ok_ ok:
	                try
	                {
		                return await bind(ok.Value).ConfigureAwait(false);
	                }
	                // ReSharper disable once RedundantCatchClause
#pragma warning disable CS0168 // Variable is declared but never used
	                catch (Exception e)
#pragma warning restore CS0168 // Variable is declared but never used
	                {
		                throw; //createGenericErrorResult
	                }
                case Error_ error:
                    return error.Convert<T1>();
                default:
                    throw new InvalidOperationException($"Unexpected derived result type: {GetType()}");
            }
        }

        public ResultSave<T1> Map<T1>(Func<T, T1> map)
            => Bind(value => Ok(map(value)));

        public Task<ResultSave<T1>> Map<T1>(Func<T, Task<T1>> map)
            => Bind(async value => Ok(await map(value).ConfigureAwait(false)));

        public T? GetValueOrDefault()
	        => Match(
		        v => (T?)v,
		        _ => default
	        );

        public T GetValueOrDefault(Func<T> defaultValue)
	        => Match(
		        v => v,
		        _ => defaultValue()
	        );

        public T GetValueOrDefault(T defaultValue)
	        => Match(
		        v => v,
		        _ => defaultValue
	        );

        public T GetValueOrThrow()
            => Match(
                v => v,
                details => throw new InvalidOperationException($"Cannot access error result value. Error: {details}"));

        public IEnumerator<T> GetEnumerator() => Match(ok => new[] { ok }, _ => Enumerable.Empty<T>()).GetEnumerator();

        public override string ToString() => Match(ok => $"Ok {ok?.ToString()}", error => $"Error {error}");
        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

        public sealed partial class Ok_ : ResultSave<T>
        {
            public T Value { get; }

            public Ok_(T value) => Value = value;

            public override ErrorDetails? GetErrorOrDefault() => null;

            public bool Equals(Ok_? other)
            {
                if (ReferenceEquals(null, other)) return false;
                if (ReferenceEquals(this, other)) return true;
                return EqualityComparer<T>.Default.Equals(Value, other.Value);
            }

            public override bool Equals(object? obj)
            {
                if (ReferenceEquals(null, obj)) return false;
                if (ReferenceEquals(this, obj)) return true;
                return obj is Ok_ other && Equals(other);
            }

            public override int GetHashCode() => Value == null ? 0 : EqualityComparer<T>.Default.GetHashCode(Value);

            public static bool operator ==(Ok_ left, Ok_ right) => Equals(left, right);

            public static bool operator !=(Ok_ left, Ok_ right) => !Equals(left, right);
        }

        public sealed partial class Error_ : ResultSave<T>
        {
            public ErrorDetails Details { get; }

            public Error_(ErrorDetails details) => Details = details;

            public ResultSave<T1>.Error_ Convert<T1>() => new ResultSave<T1>.Error_(Details);

            public override ErrorDetails? GetErrorOrDefault() => Details;

            public bool Equals(Error_? other)
            {
                if (ReferenceEquals(null, other)) return false;
                if (ReferenceEquals(this, other)) return true;
                return Equals(Details, other.Details);
            }

            public override bool Equals(object? obj)
            {
                if (ReferenceEquals(null, obj)) return false;
                if (ReferenceEquals(this, obj)) return true;
                return obj is Error_ other && Equals(other);
            }

            public override int GetHashCode() => Details.GetHashCode();

            public static bool operator ==(Error_ left, Error_ right) => Equals(left, right);

            public static bool operator !=(Error_ left, Error_ right) => !Equals(left, right);
        }

    }

    public static partial class ResultSaveExtension
    {
        #region bind

        public static async Task<ResultSave<T1>> Bind<T, T1>(
            this Task<ResultSave<T>> result,
            Func<T, ResultSave<T1>> bind)
            => (await result.ConfigureAwait(false)).Bind(bind);

        public static async Task<ResultSave<T1>> Bind<T, T1>(
            this Task<ResultSave<T>> result,
            Func<T, Task<ResultSave<T1>>> bind)
            => await (await result.ConfigureAwait(false)).Bind(bind).ConfigureAwait(false);

        #endregion

        #region map

        public static async Task<ResultSave<T1>> Map<T, T1>(
            this Task<ResultSave<T>> result,
            Func<T, T1> map)
            => (await result.ConfigureAwait(false)).Map(map);

        public static Task<ResultSave<T1>> Map<T, T1>(
            this Task<ResultSave<T>> result,
            Func<T, Task<T1>> bind)
            => Bind(result, async v => ResultSave.Ok(await bind(v).ConfigureAwait(false)));

        public static ResultSave<T> MapError<T>(this ResultSave<T> result, Func<ErrorDetails, ErrorDetails> mapError) =>
            result.Match(ok => ok, error => ResultSave.Error<T>(mapError(error)));

        #endregion

        #region match

        public static async Task<T1> Match<T, T1>(
            this Task<ResultSave<T>> result,
            Func<T, Task<T1>> ok,
            Func<ErrorDetails, Task<T1>> error)
            => await (await result.ConfigureAwait(false)).Match(ok, error).ConfigureAwait(false);

        public static async Task<T1> Match<T, T1>(
            this Task<ResultSave<T>> result,
            Func<T, Task<T1>> ok,
            Func<ErrorDetails, T1> error)
            => await (await result.ConfigureAwait(false)).Match(ok, error).ConfigureAwait(false);

        public static async Task<T1> Match<T, T1>(
            this Task<ResultSave<T>> result,
            Func<T, T1> ok,
            Func<ErrorDetails, T1> error)
            => (await result.ConfigureAwait(false)).Match(ok, error);

        #endregion

        public static ResultSave<T> Flatten<T>(this ResultSave<ResultSave<T>> result) => result.Bind(r => r);

        public static ResultSave<T1> As<T, T1>(this ResultSave<T> result, Func<ErrorDetails> errorTIsNotT1) =>
            result.Bind(r =>
            {
                if (r is T1 converted)
                    return converted;
                return ResultSave.Error<T1>(errorTIsNotT1());
            });

        public static ResultSave<T1> As<T1>(this ResultSave<object> result, Func<ErrorDetails> errorIsNotT1) =>
            result.As<object, T1>(errorIsNotT1);
        
        #region query-expression pattern
        
        public static ResultSave<T1> Select<T, T1>(this ResultSave<T> result, Func<T, T1> selector) => result.Map(selector);
        public static Task<ResultSave<T1>> Select<T, T1>(this Task<ResultSave<T>> result, Func<T, T1> selector) => result.Map(selector);
        
        public static ResultSave<T2> SelectMany<T, T1, T2>(this ResultSave<T> result, Func<T, ResultSave<T1>> selector, Func<T, T1, T2> resultSelector) => result.Bind(t => selector(t).Map(t1 => resultSelector(t, t1)));
        public static Task<ResultSave<T2>> SelectMany<T, T1, T2>(this Task<ResultSave<T>> result, Func<T, Task<ResultSave<T1>>> selector, Func<T, T1, T2> resultSelector) => result.Bind(t => selector(t).Map(t1 => resultSelector(t, t1)));
        public static Task<ResultSave<T2>> SelectMany<T, T1, T2>(this Task<ResultSave<T>> result, Func<T, ResultSave<T1>> selector, Func<T, T1, T2> resultSelector) => result.Bind(t => selector(t).Map(t1 => resultSelector(t, t1)));
        public static Task<ResultSave<T2>> SelectMany<T, T1, T2>(this ResultSave<T> result, Func<T, Task<ResultSave<T1>>> selector, Func<T, T1, T2> resultSelector) => result.Bind(t => selector(t).Map(t1 => resultSelector(t, t1)));

        #endregion
    }
}

namespace Union.Extensions
{
    public static partial class ResultSaveExtension
    {
        public static IEnumerable<T1> Choose<T, T1>(
            this IEnumerable<T> items,
            Func<T, ResultSave<T1>> choose,
            Action<ErrorDetails> onError)
            => items
                .Select(i => choose(i))
                .Choose(onError);

        public static IEnumerable<T> Choose<T>(
            this IEnumerable<ResultSave<T>> results,
            Action<ErrorDetails> onError)
            => results
                .Where(r =>
                    r.Match(_ => true, error =>
                    {
                        onError(error);
                        return false;
                    }))
                .Select(r => r.GetValueOrThrow());

        public static ResultSave<T> As<T>(this object item, Func<ErrorDetails> error) =>
            !(item is T t) ? ResultSave.Error<T>(error()) : t;

        public static ResultSave<T> NotNull<T>(this T? item, Func<ErrorDetails> error) =>
            item ?? ResultSave.Error<T>(error());

        public static ResultSave<string> NotNullOrEmpty(this string? s, Func<ErrorDetails> error)
            => string.IsNullOrEmpty(s) ? ResultSave.Error<string>(error()) : s!;

        public static ResultSave<string> NotNullOrWhiteSpace(this string? s, Func<ErrorDetails> error)
            => string.IsNullOrWhiteSpace(s) ? ResultSave.Error<string>(error()) : s!;

        public static ResultSave<T> First<T>(this IEnumerable<T> candidates, Func<T, bool> predicate, Func<ErrorDetails> noMatch) =>
            candidates
                .FirstOrDefault(i => predicate(i))
                .NotNull(noMatch);
    }
#pragma warning restore 1591
}

using System;

// ReSharper disable once CheckNamespace
namespace FunicularSwitch.Generators
{
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, Inherited = false)]
    sealed class UnionTypeAttribute : Attribute
    {
        public CaseOrder CaseOrder { get; set; } = CaseOrder.Alphabetic;
        public bool StaticFactoryMethods { get; set; } = true;
    }

    enum CaseOrder
    {
        Alphabetic,
        AsDeclared,
        Explicit
    }

    [AttributeUsage(AttributeTargets.Class, Inherited = false)]
    sealed class UnionCaseAttribute : Attribute
    {
        public UnionCaseAttribute(int index) => Index = index;

        public int Index { get; }
    }
}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/FunicularSwitch

RSCG – CommandLine

RSCG – CommandLine
 
 

name CommandLine
nuget https://www.nuget.org/packages/DotMake.CommandLine/
link https://github.com/dotmake-build/command-line
author DotMake

Generating easy command line applications.

 

This is how you can use CommandLine .

The code that you start with is


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="DotMake.CommandLine" Version="1.8.0" />
  </ItemGroup>
	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>
</Project>


The code that you will use is


// See https://aka.ms/new-console-template for more information
using DotMake.CommandLine;

Cli.Run(([CliArgument] string PersonName, int  Age) =>
{
    Console.WriteLine($@"Value for {nameof(PersonName)} parameter is '{PersonName}'");
    Console.WriteLine($@"Value for {nameof(Age)} parameter is '{Age}'");
});


Cli.Run(([CliArgument] int idData) =>
{
    Console.WriteLine($@"Value for {nameof(idData)} parameter is '{idData}'");
    
});

 

The code that is generated is

// <auto-generated />
// Generated by DotMake.CommandLine.SourceGeneration v1.8.0.0
// Roslyn (Microsoft.CodeAnalysis) v4.800.23.57201
// Generation: 1

namespace GeneratedCode
{
    /// <inheritdoc />
    [DotMake.CommandLine.CliCommandAttribute]
    public class CliCommandAsDelegate_1ra93x8 : DotMake.CommandLine.CliCommandAsDelegateDefinition
    {
        /// <inheritdoc />
        [DotMake.CommandLine.CliArgumentAttribute]
        public int idData { get; set; }

        /// <inheritdoc />
        public void Run()
        {
            InvokeDelegate
            (
                "1ra93x8",
                new object[]
                {
                    idData, 
                }
            );
        }

        [System.Runtime.CompilerServices.ModuleInitializerAttribute]
        internal static void Initialize()
        {
            // Register this definition class so that it can be found by the command as delegate hash.
            Register<GeneratedCode.CliCommandAsDelegate_1ra93x8>("1ra93x8");
        }
    }
}

// <auto-generated />
// Generated by DotMake.CommandLine.SourceGeneration v1.8.0.0
// Roslyn (Microsoft.CodeAnalysis) v4.800.23.57201
// Generation: 1

namespace GeneratedCode
{
    /// <inheritdoc />
    public class CliCommandAsDelegate_1ra93x8Builder : DotMake.CommandLine.CliCommandBuilder
    {
        /// <inheritdoc />
        public CliCommandAsDelegate_1ra93x8Builder()
        {
            DefinitionType = typeof(GeneratedCode.CliCommandAsDelegate_1ra93x8);
            ParentDefinitionType = null;
            NameCasingConvention = DotMake.CommandLine.CliNameCasingConvention.KebabCase;
            NamePrefixConvention = DotMake.CommandLine.CliNamePrefixConvention.DoubleHyphen;
            ShortFormPrefixConvention = DotMake.CommandLine.CliNamePrefixConvention.SingleHyphen;
            ShortFormAutoGenerate = true;
        }

        private GeneratedCode.CliCommandAsDelegate_1ra93x8 CreateInstance()
        {
            return new GeneratedCode.CliCommandAsDelegate_1ra93x8();
        }

        /// <inheritdoc />
        public override System.CommandLine.CliCommand Build()
        {
            // Command for 'CliCommandAsDelegate_1ra93x8' class
            var rootCommand = new System.CommandLine.CliRootCommand()
            {
            };

            var defaultClass = CreateInstance();

            // Argument for 'idData' property
            var argument0 = new System.CommandLine.CliArgument<int>
            (
                "id-data"
            )
            {
            };
            argument0.CustomParser = GetParseArgument<int>
            (
                null
            );
            argument0.DefaultValueFactory = _ => defaultClass.idData;
            rootCommand.Add(argument0);

            // Add nested or external registered children
            foreach (var child in Children)
            {
                rootCommand.Add(child.Build());
            }

            BindFunc = (parseResult) =>
            {
                var targetClass = CreateInstance();

                //  Set the parsed or default values for the options

                //  Set the parsed or default values for the arguments
                targetClass.idData = GetValueForArgument(parseResult, argument0);

                return targetClass;
            };

            rootCommand.SetAction(parseResult =>
            {
                var targetClass = (GeneratedCode.CliCommandAsDelegate_1ra93x8) BindFunc(parseResult);

                //  Call the command handler
                var cliContext = new DotMake.CommandLine.CliContext(parseResult);
                var exitCode = 0;
                targetClass.Run();
                return exitCode;
            });

            return rootCommand;
        }

        [System.Runtime.CompilerServices.ModuleInitializerAttribute]
        internal static void Initialize()
        {
            var commandBuilder = new GeneratedCode.CliCommandAsDelegate_1ra93x8Builder();

            // Register this command builder so that it can be found by the definition class
            // and it can be found by the parent definition class if it's a nested/external child.
            commandBuilder.Register();
        }
    }
}

// <auto-generated />
// Generated by DotMake.CommandLine.SourceGeneration v1.8.0.0
// Roslyn (Microsoft.CodeAnalysis) v4.800.23.57201
// Generation: 2

namespace GeneratedCode
{
    /// <inheritdoc />
    [DotMake.CommandLine.CliCommandAttribute]
    public class CliCommandAsDelegate_34pdvjr : DotMake.CommandLine.CliCommandAsDelegateDefinition
    {
        /// <inheritdoc />
        [DotMake.CommandLine.CliArgumentAttribute]
        public string PersonName { get; set; }

        /// <inheritdoc />
        [DotMake.CommandLine.CliOptionAttribute]
        public int Age { get; set; }

        /// <inheritdoc />
        public void Run()
        {
            InvokeDelegate
            (
                "34pdvjr",
                new object[]
                {
                    PersonName, 
                    Age, 
                }
            );
        }

        [System.Runtime.CompilerServices.ModuleInitializerAttribute]
        internal static void Initialize()
        {
            // Register this definition class so that it can be found by the command as delegate hash.
            Register<GeneratedCode.CliCommandAsDelegate_34pdvjr>("34pdvjr");
        }
    }
}

// <auto-generated />
// Generated by DotMake.CommandLine.SourceGeneration v1.8.0.0
// Roslyn (Microsoft.CodeAnalysis) v4.800.23.57201
// Generation: 2

namespace GeneratedCode
{
    /// <inheritdoc />
    public class CliCommandAsDelegate_34pdvjrBuilder : DotMake.CommandLine.CliCommandBuilder
    {
        /// <inheritdoc />
        public CliCommandAsDelegate_34pdvjrBuilder()
        {
            DefinitionType = typeof(GeneratedCode.CliCommandAsDelegate_34pdvjr);
            ParentDefinitionType = null;
            NameCasingConvention = DotMake.CommandLine.CliNameCasingConvention.KebabCase;
            NamePrefixConvention = DotMake.CommandLine.CliNamePrefixConvention.DoubleHyphen;
            ShortFormPrefixConvention = DotMake.CommandLine.CliNamePrefixConvention.SingleHyphen;
            ShortFormAutoGenerate = true;
        }

        private GeneratedCode.CliCommandAsDelegate_34pdvjr CreateInstance()
        {
            return new GeneratedCode.CliCommandAsDelegate_34pdvjr();
        }

        /// <inheritdoc />
        public override System.CommandLine.CliCommand Build()
        {
            // Command for 'CliCommandAsDelegate_34pdvjr' class
            var rootCommand = new System.CommandLine.CliRootCommand()
            {
            };

            var defaultClass = CreateInstance();

            // Option for 'Age' property
            var option0 = new System.CommandLine.CliOption<int>
            (
                "--age"
            )
            {
                Required = false,
            };
            option0.CustomParser = GetParseArgument<int>
            (
                null
            );
            option0.DefaultValueFactory = _ => defaultClass.Age;
            option0.Aliases.Add("-a");
            rootCommand.Add(option0);

            // Argument for 'PersonName' property
            var argument0 = new System.CommandLine.CliArgument<string>
            (
                "person-name"
            )
            {
            };
            argument0.CustomParser = GetParseArgument<string>
            (
                null
            );
            rootCommand.Add(argument0);

            // Add nested or external registered children
            foreach (var child in Children)
            {
                rootCommand.Add(child.Build());
            }

            BindFunc = (parseResult) =>
            {
                var targetClass = CreateInstance();

                //  Set the parsed or default values for the options
                targetClass.Age = GetValueForOption(parseResult, option0);

                //  Set the parsed or default values for the arguments
                targetClass.PersonName = GetValueForArgument(parseResult, argument0);

                return targetClass;
            };

            rootCommand.SetAction(parseResult =>
            {
                var targetClass = (GeneratedCode.CliCommandAsDelegate_34pdvjr) BindFunc(parseResult);

                //  Call the command handler
                var cliContext = new DotMake.CommandLine.CliContext(parseResult);
                var exitCode = 0;
                targetClass.Run();
                return exitCode;
            });

            return rootCommand;
        }

        [System.Runtime.CompilerServices.ModuleInitializerAttribute]
        internal static void Initialize()
        {
            var commandBuilder = new GeneratedCode.CliCommandAsDelegate_34pdvjrBuilder();

            // Register this command builder so that it can be found by the definition class
            // and it can be found by the parent definition class if it's a nested/external child.
            commandBuilder.Register();
        }
    }
}

// <auto-generated />
// Generated by DotMake.CommandLine.SourceGeneration v1.8.0.0
// Roslyn (Microsoft.CodeAnalysis) v4.800.23.57201
// Generation: 2

namespace GeneratedCode
{
    /// <inheritdoc />
    [DotMake.CommandLine.CliCommandAttribute]
    public class CliCommandAsDelegate_3r0zsnc : DotMake.CommandLine.CliCommandAsDelegateDefinition
    {
        /// <inheritdoc />
        [DotMake.CommandLine.CliArgumentAttribute]
        public string arg1 { get; set; }

        /// <inheritdoc />
        [DotMake.CommandLine.CliOptionAttribute]
        public bool opt1 { get; set; }

        /// <inheritdoc />
        public void Run()
        {
            InvokeDelegate
            (
                "3r0zsnc",
                new object[]
                {
                    arg1, 
                    opt1, 
                }
            );
        }

        [System.Runtime.CompilerServices.ModuleInitializerAttribute]
        internal static void Initialize()
        {
            // Register this definition class so that it can be found by the command as delegate hash.
            Register<GeneratedCode.CliCommandAsDelegate_3r0zsnc>("3r0zsnc");
        }
    }
}

// <auto-generated />
// Generated by DotMake.CommandLine.SourceGeneration v1.8.0.0
// Roslyn (Microsoft.CodeAnalysis) v4.800.23.57201
// Generation: 2

namespace GeneratedCode
{
    /// <inheritdoc />
    public class CliCommandAsDelegate_3r0zsncBuilder : DotMake.CommandLine.CliCommandBuilder
    {
        /// <inheritdoc />
        public CliCommandAsDelegate_3r0zsncBuilder()
        {
            DefinitionType = typeof(GeneratedCode.CliCommandAsDelegate_3r0zsnc);
            ParentDefinitionType = null;
            NameCasingConvention = DotMake.CommandLine.CliNameCasingConvention.KebabCase;
            NamePrefixConvention = DotMake.CommandLine.CliNamePrefixConvention.DoubleHyphen;
            ShortFormPrefixConvention = DotMake.CommandLine.CliNamePrefixConvention.SingleHyphen;
            ShortFormAutoGenerate = true;
        }

        private GeneratedCode.CliCommandAsDelegate_3r0zsnc CreateInstance()
        {
            return new GeneratedCode.CliCommandAsDelegate_3r0zsnc();
        }

        /// <inheritdoc />
        public override System.CommandLine.CliCommand Build()
        {
            // Command for 'CliCommandAsDelegate_3r0zsnc' class
            var rootCommand = new System.CommandLine.CliRootCommand()
            {
            };

            var defaultClass = CreateInstance();

            // Option for 'opt1' property
            var option0 = new System.CommandLine.CliOption<bool>
            (
                "--opt-1"
            )
            {
                Required = false,
            };
            option0.CustomParser = GetParseArgument<bool>
            (
                null
            );
            option0.DefaultValueFactory = _ => defaultClass.opt1;
            option0.Aliases.Add("-o");
            rootCommand.Add(option0);

            // Argument for 'arg1' property
            var argument0 = new System.CommandLine.CliArgument<string>
            (
                "arg-1"
            )
            {
            };
            argument0.CustomParser = GetParseArgument<string>
            (
                null
            );
            rootCommand.Add(argument0);

            // Add nested or external registered children
            foreach (var child in Children)
            {
                rootCommand.Add(child.Build());
            }

            BindFunc = (parseResult) =>
            {
                var targetClass = CreateInstance();

                //  Set the parsed or default values for the options
                targetClass.opt1 = GetValueForOption(parseResult, option0);

                //  Set the parsed or default values for the arguments
                targetClass.arg1 = GetValueForArgument(parseResult, argument0);

                return targetClass;
            };

            rootCommand.SetAction(parseResult =>
            {
                var targetClass = (GeneratedCode.CliCommandAsDelegate_3r0zsnc) BindFunc(parseResult);

                //  Call the command handler
                var cliContext = new DotMake.CommandLine.CliContext(parseResult);
                var exitCode = 0;
                targetClass.Run();
                return exitCode;
            });

            return rootCommand;
        }

        [System.Runtime.CompilerServices.ModuleInitializerAttribute]
        internal static void Initialize()
        {
            var commandBuilder = new GeneratedCode.CliCommandAsDelegate_3r0zsncBuilder();

            // Register this command builder so that it can be found by the definition class
            // and it can be found by the parent definition class if it's a nested/external child.
            commandBuilder.Register();
        }
    }
}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/CommandLine

Andrei Ignat weekly software news(mostly .NET)

* indicates required

Please select all the ways you would like to hear from me:

You can unsubscribe at any time by clicking the link in the footer of our emails. For information about our privacy practices, please visit our website.

We use Mailchimp as our marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp's privacy practices here.