< Summary

Information
Class: NexusLabs.Needlr.AgentFramework.Generators.CodeGen.AsyncLocalScopedCodeGenerator
Assembly: NexusLabs.Needlr.AgentFramework.Generators
File(s): /home/runner/work/needlr/needlr/src/NexusLabs.Needlr.AgentFramework.Generators/CodeGen/AsyncLocalScopedCodeGenerator.cs
Line coverage
95%
Covered lines: 117
Uncovered lines: 6
Coverable lines: 123
Total lines: 196
Line coverage: 95.1%
Branch coverage
94%
Covered branches: 17
Total branches: 18
Branch coverage: 94.4%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
Generate(...)100%66100%
GenerateSimplePattern(...)50%2283.78%
GenerateMutablePattern(...)100%22100%
GeneratePropertyProxies(...)100%88100%

File(s)

/home/runner/work/needlr/needlr/src/NexusLabs.Needlr.AgentFramework.Generators/CodeGen/AsyncLocalScopedCodeGenerator.cs

#LineLine coverage
 1using System.Text;
 2
 3using NexusLabs.Needlr.AgentFramework.Generators.Models;
 4
 5namespace NexusLabs.Needlr.AgentFramework.Generators.CodeGen
 6{
 7    internal static class AsyncLocalScopedCodeGenerator
 8    {
 9        public static string Generate(AsyncLocalScopedInfo info)
 10        {
 811            var sb = new StringBuilder();
 12
 813            sb.AppendLine("// <auto-generated/>");
 814            sb.AppendLine("// AsyncLocal-backed implementation for " + info.InterfaceName);
 815            sb.AppendLine("#nullable enable");
 816            sb.AppendLine();
 17
 818            if (!string.IsNullOrEmpty(info.NamespaceName))
 19            {
 820                sb.AppendLine("namespace " + info.NamespaceName);
 821                sb.AppendLine("{");
 22            }
 23
 824            sb.AppendLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"NexusLabs.Needlr.AgentFramework.Gen
 825            sb.AppendLine("internal sealed class " + info.GeneratedClassName + " : " + info.InterfaceFullName);
 826            sb.AppendLine("{");
 27
 828            if (info.IsMutable)
 29            {
 530                GenerateMutablePattern(sb, info);
 31            }
 32            else
 33            {
 334                GenerateSimplePattern(sb, info);
 35            }
 36
 837            sb.AppendLine("}");
 38
 839            if (!string.IsNullOrEmpty(info.NamespaceName))
 40            {
 841                sb.AppendLine("}");
 42            }
 43
 844            return sb.ToString();
 45        }
 46
 47        private static void GenerateSimplePattern(StringBuilder sb, AsyncLocalScopedInfo info)
 48        {
 349            var valueType = info.ValueTypeFullName;
 50
 351            sb.AppendLine("    private static readonly global::System.Threading.AsyncLocal<" + valueType + "?> _current 
 352            sb.AppendLine();
 353            sb.AppendLine("    public " + valueType + "? Current => _current.Value;");
 354            sb.AppendLine();
 55
 356            GeneratePropertyProxies(sb, info, "_current.Value");
 57
 358            if (info.HasScopeParameter)
 59            {
 360                sb.AppendLine("    public global::System.IDisposable " + info.ScopeMethodName + "(" + info.ScopeParamete
 361                sb.AppendLine("    {");
 362                sb.AppendLine("        if (value == null) throw new global::System.ArgumentNullException(nameof(value));
 363                sb.AppendLine();
 364                sb.AppendLine("        var previous = _current.Value;");
 365                sb.AppendLine("        _current.Value = value;");
 366                sb.AppendLine("        return new Scope(previous);");
 367                sb.AppendLine("    }");
 68            }
 69            else
 70            {
 071                sb.AppendLine("    public global::System.IDisposable " + info.ScopeMethodName + "()");
 072                sb.AppendLine("    {");
 073                sb.AppendLine("        var previous = _current.Value;");
 074                sb.AppendLine("        _current.Value = default;");
 075                sb.AppendLine("        return new Scope(previous);");
 076                sb.AppendLine("    }");
 77            }
 78
 379            sb.AppendLine();
 380            sb.AppendLine("    private sealed class Scope : global::System.IDisposable");
 381            sb.AppendLine("    {");
 382            sb.AppendLine("        private readonly " + valueType + "? _previous;");
 383            sb.AppendLine("        private bool _disposed;");
 384            sb.AppendLine();
 385            sb.AppendLine("        public Scope(" + valueType + "? previous) => _previous = previous;");
 386            sb.AppendLine();
 387            sb.AppendLine("        public void Dispose()");
 388            sb.AppendLine("        {");
 389            sb.AppendLine("            if (_disposed) return;");
 390            sb.AppendLine("            _disposed = true;");
 391            sb.AppendLine("            _current.Value = _previous;");
 392            sb.AppendLine("        }");
 393            sb.AppendLine("    }");
 394        }
 95
 96        private static void GenerateMutablePattern(StringBuilder sb, AsyncLocalScopedInfo info)
 97        {
 598            var valueType = info.ValueTypeFullName;
 99
 5100            sb.AppendLine("    private static readonly global::System.Threading.AsyncLocal<Holder?> _current = new globa
 5101            sb.AppendLine();
 5102            sb.AppendLine("    public " + valueType + "? Current => _current.Value?.Value;");
 5103            sb.AppendLine();
 104
 5105            GeneratePropertyProxies(sb, info, "_current.Value?.Value");
 106
 5107            if (info.HasScopeParameter)
 108            {
 3109                sb.AppendLine("    public global::System.IDisposable " + info.ScopeMethodName + "(" + info.ScopeParamete
 3110                sb.AppendLine("    {");
 3111                sb.AppendLine("        var previous = _current.Value;");
 3112                sb.AppendLine("        _current.Value = new Holder() { Value = value };");
 3113                sb.AppendLine("        return new Scope(previous);");
 3114                sb.AppendLine("    }");
 115            }
 116            else
 117            {
 2118                sb.AppendLine("    public global::System.IDisposable " + info.ScopeMethodName + "()");
 2119                sb.AppendLine("    {");
 2120                sb.AppendLine("        var previous = _current.Value;");
 2121                sb.AppendLine("        _current.Value = new Holder();");
 2122                sb.AppendLine("        return new Scope(previous);");
 2123                sb.AppendLine("    }");
 124            }
 125
 5126            sb.AppendLine();
 5127            sb.AppendLine("    internal void Set(" + valueType + " value)");
 5128            sb.AppendLine("    {");
 5129            sb.AppendLine("        var holder = _current.Value;");
 5130            sb.AppendLine("        if (holder != null)");
 5131            sb.AppendLine("        {");
 5132            sb.AppendLine("            holder.Value = value;");
 5133            sb.AppendLine("        }");
 5134            sb.AppendLine("        else");
 5135            sb.AppendLine("        {");
 5136            sb.AppendLine("            _current.Value = new Holder() { Value = value };");
 5137            sb.AppendLine("        }");
 5138            sb.AppendLine("    }");
 5139            sb.AppendLine();
 140
 5141            sb.AppendLine("    private sealed class Holder");
 5142            sb.AppendLine("    {");
 5143            sb.AppendLine("        public " + valueType + "? Value;");
 5144            sb.AppendLine("    }");
 5145            sb.AppendLine();
 146
 5147            sb.AppendLine("    private sealed class Scope : global::System.IDisposable");
 5148            sb.AppendLine("    {");
 5149            sb.AppendLine("        private readonly Holder? _previous;");
 5150            sb.AppendLine("        private bool _disposed;");
 5151            sb.AppendLine();
 5152            sb.AppendLine("        public Scope(Holder? previous) => _previous = previous;");
 5153            sb.AppendLine();
 5154            sb.AppendLine("        public void Dispose()");
 5155            sb.AppendLine("        {");
 5156            sb.AppendLine("            if (_disposed) return;");
 5157            sb.AppendLine("            _disposed = true;");
 5158            sb.AppendLine("            _current.Value = _previous;");
 5159            sb.AppendLine("        }");
 5160            sb.AppendLine("    }");
 5161        }
 162
 163        private static void GeneratePropertyProxies(
 164            StringBuilder sb,
 165            AsyncLocalScopedInfo info,
 166            string currentExpression)
 167        {
 8168            if (info.ProxyProperties.Length == 0)
 4169                return;
 170
 24171            foreach (var prop in info.ProxyProperties)
 172            {
 8173                var getExpr = currentExpression + "?." + prop.Name;
 8174                if (prop.IsNonNullableValueType)
 175                {
 4176                    getExpr += " ?? default";
 177                }
 178
 8179                if (prop.HasSetter)
 180                {
 5181                    sb.AppendLine("    public " + prop.TypeFullName + " " + prop.Name);
 5182                    sb.AppendLine("    {");
 5183                    sb.AppendLine("        get => " + getExpr + ";");
 5184                    sb.AppendLine("        set { if (" + currentExpression + " is { } h) h." + prop.Name + " = value; }"
 5185                    sb.AppendLine("    }");
 186                }
 187                else
 188                {
 3189                    sb.AppendLine("    public " + prop.TypeFullName + " " + prop.Name + " => " + getExpr + ";");
 190                }
 191
 8192                sb.AppendLine();
 193            }
 4194        }
 195    }
 196}