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

More than100 examples of RSCG

I am deeply into Roslyn Source Code Generators. I like when code generates code . So I have created ones and make inventory of others  . You can find the examples here by date : https://ignatandrei.github.io/RSCG_Examples/v2/docs/List-of-RSCG  and by category https://ignatandrei.github.io/RSCG_Examples/v2/docs/rscg-examples 

I have started when they appeared, with ISourceGenerator and now I am fond , even if the interface

IIncrementalGenerator https://learn.microsoft.com/en-us/dotnet/api/microsoft.codeanalysis.iincrementalgenerator?view=roslyn-dotnet-4.7.0 adds some learning curve.

Some thoughts:

  1. Being code ( even generated) has , obvious, bugs. Can be improved
  2. Microsoft is pushing those –  for the moments, are 12 : List of RSCG | RSCG Examples (ignatandrei.github.io) . However, for AOT , seems to be the direction
  3. Some are just glorified interpretors – for example, 103 – HangfireRecurringJob | RSCG Examples (ignatandrei.github.io) – translates an attribute to code. But also does a Mapper
  4. There are many ways to do same thing – see mapper category again at 105 RSCG by category | RSCG Examples (ignatandrei.github.io)
  5. To see the generated files put into the .csproj

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

Aspire & Intro to GitHub Copilot

Azi , la 19:30

Prezentare 1: Aspire – running multiple services
Descriere: Utilizare Aspire pentru vizualizare multiple servicii, open telemetry, frontend , backend, containere
Prezentator: Andrei Ignat, http://msprogrammer.serviciipeweb.ro/

Prezentare 2: Intro to GitHub Copilot
Descriere : Voi prezenta:
– la ce am folosit eu Copilot
– ⁠ce e nou în Copilot Chat
– ⁠vom încerca să facem un joc de la zero – doar cu cod generat pe baza unei prezentări de la MS Build 2023 –
Prezentator : Julian (Iulian) Atanasoae , https://www.linkedin.com/in/julianatanasoae/

Va astept la https://meet.google.com/pmm-oeyc-dmy

RSCG – NetAutomaticInterface

RSCG – NetAutomaticInterface
 
 

name NetAutomaticInterface
nuget https://www.nuget.org/packages/AutomaticInterface/
link https://github.com/codecentric/net_automatic_interface
author codecentric AG

GEnerating interface from class

 

This is how you can use NetAutomaticInterface .

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="AutomaticInterface" Version="2.1.0" OutputItemType="Analyzer" >
	    <PrivateAssets>all</PrivateAssets>
	    <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
	  </PackageReference>
	</ItemGroup>
</Project>


The code that you will use is


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

Console.WriteLine("Hello, World!");
IPerson person=new Person();
person.FirstName="Andrei";
person.LastName="Ignat";
Console.WriteLine(person.FullName());


[AttributeUsage(AttributeTargets.Class)]
public class GenerateAutomaticInterfaceAttribute : Attribute
{
    public GenerateAutomaticInterfaceAttribute(string namespaceName = "") { }
}


namespace Class2Interface;
[GenerateAutomaticInterface("Class2Interface")]
internal class Person:IPerson
{
    public int ID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Name
    {
        get
        {
            return $"{FirstName} {LastName}";
        }
    }
    public string FullName()=>$"{FirstName} {LastName}";
}


 

The code that is generated is

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

using System.CodeDom.Compiler;

namespace Class2Interface
{
    [GeneratedCode("AutomaticInterface", "")]
    public partial interface IPerson
    {
        /// <inheritdoc />
        int ID { get; set; }
        
        /// <inheritdoc />
        string FirstName { get; set; }
        
        /// <inheritdoc />
        string LastName { get; set; }
        
        /// <inheritdoc />
        string Name { get; }
        
        /// <inheritdoc />
        string FullName();
        
    }
}

Code and pdf at

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

RSCG – WhatIAmDoing

RSCG – WhatIAmDoing
 
 

name WhatIAmDoing
nuget https://www.nuget.org/packages/RSCG_WhatIAmDoing/
https://www.nuget.org/packages/RSCG_WhatIAmDoing_Common/
link https://github.com/ignatandrei/RSCG_WhatIAmDoing
author Ignat Andrei

Intercept any method in any project

 

This is how you can use WhatIAmDoing .

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="Microsoft.Extensions.Caching.Memory" Version="8.0.0" /> -->
		<PackageReference Include="RSCG_WhatIAmDoing" Version="8.2024.10201.735" />
		<PackageReference Include="RSCG_WhatIAmDoing_Common" Version="8.2024.10201.735" />
	</ItemGroup>

	<PropertyGroup>

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

</Project>


The code that you will use is


using RSCG_WhatIAmDoing_Common;

Console.WriteLine("Hello, World!");
var data = CachingData.Methods().ToArray();

foreach (var item in data)
{
    Console.WriteLine($"Method {item.typeAndMethodData.MethodName} from class {item.typeAndMethodData.TypeOfClass} Time: {item.StartedAtDate} state {item.State} ");
    Console.WriteLine($"  =>Arguments: {item.ArgumentsAsString()}");
    if ((item.State & AccumulatedStateMethod.HasResult) == AccumulatedStateMethod.HasResult)
    {
        Console.WriteLine($"  =>Result: {item.Result}");
    }

}



using RSCG_WhatIAmDoing;
using RSCG_WhatIAmDoing_Common;

namespace WIADDemo;
//[ExposeClass(typeof(Encoding), nameof(Encoding.EncodingName))]
[InterceptStatic("System.Console.*")] // regex
internal class InterceptorMethodStatic : InterceptorMethodStaticBase, IInterceptorMethodStatic
{

}

 

The code that is generated is

#pragma warning disable CS1591 
#pragma warning disable CS9113
namespace System.Runtime.CompilerServices{
[AttributeUsage(AttributeTargets.Method,AllowMultiple =true)]
file class InterceptsLocationAttribute(string filePath, int line, int character) : Attribute
{
}
}//end namespace

namespace RSCG_InterceptorTemplate{
static partial class SimpleIntercept
{


//replace code:Console.WriteLine("Hello, World!");
//replace code:123456789!123456789!123456789!12345
[System.Runtime.CompilerServices.InterceptsLocation(@"D:\gth\RSCG_Examples\v2\rscg_examples\WIAD\src\WIADDemo\WIADDemo\Program.cs", 3, 9)]
    
//[System.Diagnostics.DebuggerStepThrough()]
public static  void Intercept__WriteLine_0 (  string? value )  
{

System.Collections.Generic.Dictionary<string,string?> valValues = new (){


};//end valValues

System.Collections.Generic.Dictionary<string,string?> stringValues = new() {


                { "value", value  ?.ToString() } ,
            };//end stringValues

        System.Collections.Generic.Dictionary<string,string?>
            expValues = new() {

            };//end exposeValues


        var x=WIADDemo.InterceptorMethodStatic .InterceptStaticMethodBefore(
$$$""""
    {"IsVoid":true,"Tag":"","MethodName":"WriteLine","TypeOfClass":"System.Console"}
""""
            ,valValues , stringValues , expValues
)
; 


                try{

                 System.Console.WriteLine(value);

                WIADDemo.InterceptorMethodStatic .InterceptMethodAfterWithoutResult(x);
                
                }

            
        
            catch(System.Exception ex){
                WIADDemo.InterceptorMethodStatic .InterceptMethodException(x,ex);
                throw;
            }
            finally{
                WIADDemo.InterceptorMethodStatic .InterceptMethodFinally(x);
            }
        
}
                

}//end class

}//namespace RSCG_InterceptorTemplate


#pragma warning disable CS1591 
#pragma warning disable CS9113
namespace System.Runtime.CompilerServices{
[AttributeUsage(AttributeTargets.Method,AllowMultiple =true)]
file class InterceptsLocationAttribute(string filePath, int line, int character) : Attribute
{
}
}//end namespace

namespace RSCG_InterceptorTemplate{
static partial class SimpleIntercept
{


//replace code:    Console.WriteLine($"Method {item.typeAndMethodData.MethodName} from class {item.typeAndMethodData.TypeOfClass} Time: {item.StartedAtDate} state {item.State} ");
//replace code:123456789!123456789!123456789!123456789!123456789!123456789!123456789!123456789!123456789!123456789!123456789!123456789!123456789!123456789!123456789!123456789!1234
[System.Runtime.CompilerServices.InterceptsLocation(@"D:\gth\RSCG_Examples\v2\rscg_examples\WIAD\src\WIADDemo\WIADDemo\Program.cs", 8, 13)]
    
//[System.Diagnostics.DebuggerStepThrough()]
public static  void Intercept__WriteLine_1 (  string? value )  
{

System.Collections.Generic.Dictionary<string,string?> valValues = new (){


};//end valValues

System.Collections.Generic.Dictionary<string,string?> stringValues = new() {


                { "value", value  ?.ToString() } ,
            };//end stringValues

        System.Collections.Generic.Dictionary<string,string?>
            expValues = new() {

            };//end exposeValues


        var x=WIADDemo.InterceptorMethodStatic .InterceptStaticMethodBefore(
$$$""""
    {"IsVoid":true,"Tag":"","MethodName":"WriteLine","TypeOfClass":"System.Console"}
""""
            ,valValues , stringValues , expValues
)
; 


                try{

                 System.Console.WriteLine(value);

                WIADDemo.InterceptorMethodStatic .InterceptMethodAfterWithoutResult(x);
                
                }

            
        
            catch(System.Exception ex){
                WIADDemo.InterceptorMethodStatic .InterceptMethodException(x,ex);
                throw;
            }
            finally{
                WIADDemo.InterceptorMethodStatic .InterceptMethodFinally(x);
            }
        
}
                

}//end class

}//namespace RSCG_InterceptorTemplate


#pragma warning disable CS1591 
#pragma warning disable CS9113
namespace System.Runtime.CompilerServices{
[AttributeUsage(AttributeTargets.Method,AllowMultiple =true)]
file class InterceptsLocationAttribute(string filePath, int line, int character) : Attribute
{
}
}//end namespace

namespace RSCG_InterceptorTemplate{
static partial class SimpleIntercept
{


//replace code:    Console.WriteLine($"  =>Arguments: {item.ArgumentsAsString()}");
//replace code:123456789!123456789!123456789!123456789!123456789!123456789!12345678
[System.Runtime.CompilerServices.InterceptsLocation(@"D:\gth\RSCG_Examples\v2\rscg_examples\WIAD\src\WIADDemo\WIADDemo\Program.cs", 9, 13)]
    
//[System.Diagnostics.DebuggerStepThrough()]
public static  void Intercept__WriteLine_2 (  string? value )  
{

System.Collections.Generic.Dictionary<string,string?> valValues = new (){


};//end valValues

System.Collections.Generic.Dictionary<string,string?> stringValues = new() {


                { "value", value  ?.ToString() } ,
            };//end stringValues

        System.Collections.Generic.Dictionary<string,string?>
            expValues = new() {

            };//end exposeValues


        var x=WIADDemo.InterceptorMethodStatic .InterceptStaticMethodBefore(
$$$""""
    {"IsVoid":true,"Tag":"","MethodName":"WriteLine","TypeOfClass":"System.Console"}
""""
            ,valValues , stringValues , expValues
)
; 


                try{

                 System.Console.WriteLine(value);

                WIADDemo.InterceptorMethodStatic .InterceptMethodAfterWithoutResult(x);
                
                }

            
        
            catch(System.Exception ex){
                WIADDemo.InterceptorMethodStatic .InterceptMethodException(x,ex);
                throw;
            }
            finally{
                WIADDemo.InterceptorMethodStatic .InterceptMethodFinally(x);
            }
        
}
                

}//end class

}//namespace RSCG_InterceptorTemplate


#pragma warning disable CS1591 
#pragma warning disable CS9113
namespace System.Runtime.CompilerServices{
[AttributeUsage(AttributeTargets.Method,AllowMultiple =true)]
file class InterceptsLocationAttribute(string filePath, int line, int character) : Attribute
{
}
}//end namespace

namespace RSCG_InterceptorTemplate{
static partial class SimpleIntercept
{


//replace code:        Console.WriteLine($"  =>Result: {item.Result}");
//replace code:123456789!123456789!123456789!123456789!123456789!123456
[System.Runtime.CompilerServices.InterceptsLocation(@"D:\gth\RSCG_Examples\v2\rscg_examples\WIAD\src\WIADDemo\WIADDemo\Program.cs", 12, 17)]
    
//[System.Diagnostics.DebuggerStepThrough()]
public static  void Intercept__WriteLine_3 (  string? value )  
{

System.Collections.Generic.Dictionary<string,string?> valValues = new (){


};//end valValues

System.Collections.Generic.Dictionary<string,string?> stringValues = new() {


                { "value", value  ?.ToString() } ,
            };//end stringValues

        System.Collections.Generic.Dictionary<string,string?>
            expValues = new() {

            };//end exposeValues


        var x=WIADDemo.InterceptorMethodStatic .InterceptStaticMethodBefore(
$$$""""
    {"IsVoid":true,"Tag":"","MethodName":"WriteLine","TypeOfClass":"System.Console"}
""""
            ,valValues , stringValues , expValues
)
; 


                try{

                 System.Console.WriteLine(value);

                WIADDemo.InterceptorMethodStatic .InterceptMethodAfterWithoutResult(x);
                
                }

            
        
            catch(System.Exception ex){
                WIADDemo.InterceptorMethodStatic .InterceptMethodException(x,ex);
                throw;
            }
            finally{
                WIADDemo.InterceptorMethodStatic .InterceptMethodFinally(x);
            }
        
}
                

}//end class

}//namespace RSCG_InterceptorTemplate


Code and pdf at

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

RSCG – NotNotAppSettings

RSCG – NotNotAppSettings
 
 

name NotNotAppSettings
nuget https://www.nuget.org/packages/NotNot.AppSettings/
link https://github.com/jasonswearingen/NotNot.AppSettings/
author jasonswearingen

Application Settings to strongly typed classes. Generate also from AppSettings development

 

This is how you can use NotNotAppSettings .

The code that you start with is


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

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <InvariantGlobalization>true</InvariantGlobalization>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.1" />
    <PackageReference Include="NotNot.AppSettings" Version="1.0.0" OutputItemType="Analyzer" ReferenceOutputAssembly="true" />
    <PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
  </ItemGroup>

  <ItemGroup>
    <AdditionalFiles Update="appsettings.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </AdditionalFiles>
  </ItemGroup>
	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>
</Project>


The code that you will use is


var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddSingleton<TestAppSettings.AppSettingsGen.IAppSettingsBinder, TestAppSettings.AppSettingsGen.AppSettingsBinder>();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

var summaries = new[]
{
    "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};

var appSettings = app.Services.GetRequiredService<TestAppSettings.AppSettingsGen.IAppSettingsBinder>().AppSettings;
Console.WriteLine(appSettings.AppName);

app.MapGet("/weatherforecast", () =>
{
    var forecast = Enumerable.Range(1, 5).Select(index =>
        new WeatherForecast
        (
            DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
            Random.Shared.Next(-20, 55),
            summaries[Random.Shared.Next(summaries.Length)]
        ))
        .ToArray();
    return forecast;
})
.WithName("GetWeatherForecast")
.WithOpenApi();

app.Run();

internal record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}


 

The code that is generated is


/** 
 * This file is generated by the NotNot.AppSettings nuget package.
 * Do not edit this file directly, instead edit the appsettings.json files and rebuild the project.
**/
using System;
using System.Runtime.CompilerServices;
namespace TestAppSettings.AppSettingsGen;

[CompilerGenerated]
public partial class AppSettings {
   public TestAppSettings.AppSettingsGen._AppSettings.Logging? Logging{get; set;}
   public string? AllowedHosts{get; set;}
   public string? AppName{get; set;}

}


/** 
 * This file is generated by the NotNot.AppSettings nuget package.
 * Do not edit this file directly, instead edit the appsettings.json files and rebuild the project.
**/
using System;
using System.Runtime.CompilerServices;
namespace TestAppSettings.AppSettingsGen._AppSettings;

[CompilerGenerated]
public partial class Logging {
   public TestAppSettings.AppSettingsGen._AppSettings._Logging.LogLevel? LogLevel{get; set;}

}


/** 
 * This file is generated by the NotNot.AppSettings nuget package.
 * Do not edit this file directly, instead edit the appsettings.json files and rebuild the project.
**/
using System;
using System.Runtime.CompilerServices;
namespace TestAppSettings.AppSettingsGen._AppSettings._Logging;

[CompilerGenerated]
public partial class LogLevel {
   public string? Default{get; set;}
   public string? Microsoft_AspNetCore{get; set;}

}


/** 
 * This file is generated by the NotNot.AppSettings nuget package.
 * Do not edit this file directly, instead edit the appsettings.json files and rebuild the project.
**/

using Microsoft.Extensions.Configuration;
namespace TestAppSettings.AppSettingsGen;


/// <summary>
/// Strongly typed AppSettings.json, recreated every build. 
/// <para>You can use this directly, extend it (it's a partial class), 
/// or get a populated instance of it via the <see cref="AppSettingsBinder"/> DI service</para>
/// </summary>
public partial class AppSettings
{
}

/// <summary>
/// a DI service that contains a strongly-typed copy of your appsettings.json
/// <para><strong>DI Usage:</strong></para>
/// <para><c>builder.Services.AddSingleton&lt;IAppSettingsBinder, AppSettingsBinder&gt;();</c></para>
/// <para><c>var app = builder.Build();</c></para>
///  <para><c>var appSettings = app.Services.GetRequiredService&lt;IAppSettingsBinder&gt;().AppSettings;</c></para>
/// <para><strong>Non-DI Usage:</strong></para>
/// <para><c>var appSettings = AppSettingsBinder.LoadDirect();</c></para>
/// </summary>
public partial class AppSettingsBinder : IAppSettingsBinder
{
   public AppSettings AppSettings { get; protected set; }

   public AppSettingsBinder(IConfiguration _config)
   {
      AppSettings = new AppSettings();

      //automatically reads and binds to config file
      _config.Bind(AppSettings);
   }

   /// <summary>
   /// Manually construct an AppSettings from your appsettings.json files.
   /// <para>NOTE: This method is provided for non-DI users.  If you use DI, don't use this method.  Instead just register this class as a service.</para>
   /// </summary>
   /// <param name="appSettingsLocation">folder where to search for appsettings.json.  defaults to current app folder.</param>
   /// <param name="appSettingsFileNames">lets you override the files to load up.  defaults to 'appsettings.json' and 'appsettings.{DOTNET_ENVIRONMENT}.json'</param>
   /// <param name="throwIfFilesMissing">default is to silently ignore if any of the .json files are missing.</param>
   /// <returns>your strongly typed appsettings with values from your .json loaded in</returns>
   public static AppSettings LoadDirect(string? appSettingsLocation = null,IEnumerable<string>? appSettingsFileNames=null,bool throwIfFilesMissing=false )
   {      
      //pick what .json files to load
      if (appSettingsFileNames is null)
      {
         //figure out what env
         var env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
         env ??= Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT");
         env ??= Environment.GetEnvironmentVariable("ENVIRONMENT");
         //env ??= "Development"; //default to "Development
         if (env is null)
         {
            appSettingsFileNames = new[] { "appsettings.json" };
         }
         else
         {
            appSettingsFileNames = new[] { "appsettings.json", $"appsettings.{env}.json" };
         }
      }

      //build a config from the specified files
      var builder = new ConfigurationBuilder();
      if (appSettingsLocation != null)
      {
         builder.SetBasePath(appSettingsLocation);
      }
      var optional = !throwIfFilesMissing;
      foreach (var fileName in appSettingsFileNames)
      {         
         builder.AddJsonFile(fileName, optional: optional, reloadOnChange: false); // Add appsettings.json
      }
      IConfigurationRoot configuration = builder.Build();

      //now finally get the appsettings we care about
      var binder = new AppSettingsBinder(configuration);
      return binder.AppSettings;
   }
}

/// <summary>
/// a DI service that contains a strongly-typed copy of your appsettings.json
/// <para><strong>DI Usage:</strong></para>
/// <para><c>builder.Services.AddSingleton&lt;IAppSettingsBinder, AppSettingsBinder&gt;();</c></para>
/// <para><c>var app = builder.Build();</c></para>
///  <para><c>var appSettings = app.Services.GetRequiredService&lt;IAppSettingsBinder&gt;().AppSettings;</c></para>
/// <para><strong>Non-DI Usage:</strong></para>
/// <para><c>var appSettings = AppSettingsBinder.LoadDirect();</c></para>
/// </summary>
public interface IAppSettingsBinder
{
   public AppSettings AppSettings { get; }
}

Code and pdf at

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

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.