diff --git a/Tools/Substrate.DotNet/Service/Generators/Base/SolutionGeneratorBase.cs b/Tools/Substrate.DotNet/Service/Generators/Base/SolutionGeneratorBase.cs index c70804d..56935b1 100644 --- a/Tools/Substrate.DotNet/Service/Generators/Base/SolutionGeneratorBase.cs +++ b/Tools/Substrate.DotNet/Service/Generators/Base/SolutionGeneratorBase.cs @@ -94,7 +94,6 @@ protected NodeTypeResolver GenerateTypes(Dictionary nodeTypes, s break; // Handled by type resolver } } - return resolver; } @@ -102,15 +101,6 @@ private void CallVariant(string variantType, NodeTypeVariant nodeType, ref NodeT { switch (variantType) { - case "Runtime": - { - RunetimeBuilder.Init(ProjectName, nodeType.Id, nodeType, typeDict).Create().Build(write: write, out bool success, basePath); - if (!success) - { - Logger.Error($"Could not build type {nodeType.Id}!"); - } - break; - } case "Enum": { EnumBuilder.Init(ProjectName, nodeType.Id, nodeType, typeDict).Create().Build(write: write, out bool success, basePath); @@ -121,17 +111,26 @@ private void CallVariant(string variantType, NodeTypeVariant nodeType, ref NodeT break; } - default: + + case "Option": + // TODO (darkfriend77) ??? break; + + case "Void": + // TODO (darkfriend77) ??? + break; + + default: + throw new NotSupportedException($"Unknown variant type {variantType}"); } } private static Dictionary GetRuntimeIndex(Dictionary nodeTypes, string runtime, string runtimeType) { - NodeType nodeType = nodeTypes.Select(p => p.Value).Where(p => p.Path != null && p.Path.Length == 2 && p.Path[0] == runtime && p.Path[1] == runtimeType).FirstOrDefault(); - if (nodeType is null or not NodeTypeVariant) + NodeType nodeType = nodeTypes.Select(p => p.Value).FirstOrDefault(p => p.Path != null && p.Path.Length == 2 && p.Path[0] == runtime && p.Path[1] == runtimeType); + if (nodeType is not NodeTypeVariant) { - throw new Exception($"Node Index changed for {runtime}.{runtimeType} and {nodeType.GetType().Name}"); + throw new NotSupportedException($"Node Index changed for {runtime}.{runtimeType} and {nodeType?.GetType().Name}"); } Dictionary result = new(); @@ -196,7 +195,7 @@ protected static void GetGenericStructs(Dictionary nodeTypes) if (generics.Contains(key)) { - type.Path[^1] = type.Path[^1] + "T" + (_countPaths.ContainsKey(key) ? _countPaths[key] : 1); + type.Path[^1] = $"{type.Path[^1]}T{(_countPaths.ContainsKey(key) ? _countPaths[key] : 1)}"; } } } diff --git a/Tools/Substrate.DotNet/Service/Node/ArrayBuilder.cs b/Tools/Substrate.DotNet/Service/Node/ArrayBuilder.cs index b6153d9..e4440d8 100644 --- a/Tools/Substrate.DotNet/Service/Node/ArrayBuilder.cs +++ b/Tools/Substrate.DotNet/Service/Node/ArrayBuilder.cs @@ -1,66 +1,72 @@ -using Substrate.DotNet.Service.Node.Base; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Substrate.DotNet.Service.Node.Base; using Substrate.NetApi.Model.Meta; -using System; -using System.CodeDom; -using System.Collections.Generic; using System.Linq; -using System.Reflection; namespace Substrate.DotNet.Service.Node { public class ArrayBuilder : TypeBuilderBase { public static int Counter = 0; + private ArrayBuilder(string projectName, uint id, NodeTypeArray typeDef, NodeTypeResolver typeDict) : base(projectName, id, typeDef, typeDict) { } - private static CodeMemberMethod GetDecode(string baseType) + private static MethodDeclarationSyntax GetDecodeRoslyn(string baseType) { - CodeMemberMethod decodeMethod = SimpleMethod("Decode"); - CodeParameterDeclarationExpression param1 = new() - { - Type = new CodeTypeReference("System.Byte[]"), - Name = "byteArray" - }; - decodeMethod.Parameters.Add(param1); - CodeParameterDeclarationExpression param2 = new() - { - Type = new CodeTypeReference("System.Int32"), - Name = "p", - Direction = FieldDirection.Ref - }; - decodeMethod.Parameters.Add(param2); - decodeMethod.Statements.Add(new CodeSnippetExpression("var start = p")); - decodeMethod.Statements.Add(new CodeSnippetExpression($"var array = new {baseType}[TypeSize]")); - decodeMethod.Statements.Add(new CodeSnippetExpression("for (var i = 0; i < array.Length; i++) " + + MethodDeclarationSyntax decodeMethod = SyntaxFactory.MethodDeclaration(SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.VoidKeyword)), "Decode") + .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) + .AddModifiers(SyntaxFactory.Token(SyntaxKind.OverrideKeyword)) + .AddParameterListParameters( + SyntaxFactory.Parameter(SyntaxFactory.Identifier("byteArray")).WithType(SyntaxFactory.ParseTypeName("byte[]")), + SyntaxFactory.Parameter(SyntaxFactory.Identifier("p")).WithType(SyntaxFactory.ParseTypeName("int")).WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.RefKeyword)))) + .WithBody(SyntaxFactory.Block()); + + decodeMethod = decodeMethod.AddBodyStatements(SyntaxFactory.ParseStatement("var start = p;")); + + decodeMethod = decodeMethod.AddBodyStatements( + SyntaxFactory.ParseStatement($"var array = new {baseType}[TypeSize];")); + + decodeMethod = decodeMethod.AddBodyStatements( + SyntaxFactory.ParseStatement("for (var i = 0; i < array.Length; i++) " + "{" + $"var t = new {baseType}();" + "t.Decode(byteArray, ref p);" + "array[i] = t;" + "}")); - decodeMethod.Statements.Add(new CodeSnippetExpression("var bytesLength = p - start")); - decodeMethod.Statements.Add(new CodeSnippetExpression("Bytes = new byte[bytesLength]")); - decodeMethod.Statements.Add(new CodeSnippetExpression("System.Array.Copy(byteArray, start, Bytes, 0, bytesLength)")); - decodeMethod.Statements.Add(new CodeSnippetExpression("Value = array")); + + decodeMethod = decodeMethod.AddBodyStatements( + SyntaxFactory.ParseStatement("var bytesLength = p - start;")); + decodeMethod = decodeMethod.AddBodyStatements( + SyntaxFactory.ParseStatement("Bytes = new byte[bytesLength];")); + decodeMethod = decodeMethod.AddBodyStatements( + SyntaxFactory.ParseStatement("System.Array.Copy(byteArray, start, Bytes, 0, bytesLength);")); + decodeMethod = decodeMethod.AddBodyStatements( + SyntaxFactory.ParseStatement("Value = array;")); return decodeMethod; } - private static CodeMemberMethod GetEncode() + private static MethodDeclarationSyntax GetEncodeRoslyn() { - CodeMemberMethod encodeMethod = new() - { - Attributes = MemberAttributes.Public | MemberAttributes.Override, - Name = "Encode", - ReturnType = new CodeTypeReference("System.Byte[]") - }; - encodeMethod.Statements.Add(new CodeSnippetExpression("var result = new List()")); - encodeMethod.Statements.Add(new CodeSnippetExpression("foreach (var v in Value)" + - "{" + - "result.AddRange(v.Encode());" + - "}")); - encodeMethod.Statements.Add(new CodeSnippetExpression("return result.ToArray()")); + MethodDeclarationSyntax encodeMethod = SyntaxFactory.MethodDeclaration(SyntaxFactory.ParseTypeName("System.Byte[]"), "Encode") + .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.OverrideKeyword)) + .WithBody(SyntaxFactory.Block()); + + encodeMethod = encodeMethod + .AddBodyStatements(SyntaxFactory.ParseStatement("var result = new List();")); + + encodeMethod = encodeMethod.AddBodyStatements( + SyntaxFactory.ParseStatement("foreach (var v in Value)" + + "{" + + "result.AddRange(v.Encode());" + + "}")); + + encodeMethod = encodeMethod.AddBodyStatements(SyntaxFactory.ParseStatement("return result.ToArray();")); + return encodeMethod; } @@ -77,111 +83,92 @@ public override TypeBuilderBase Create() ClassName = $"Arr{typeDef.Length}{fullItem.ClassName}"; - CodeNamespace typeNamespace = new(NamespaceName); - TargetUnit.Namespaces.Add(typeNamespace); - if (ClassName.Any(ch => !char.IsLetterOrDigit(ch))) { + // TODO: check if this is a valid solution Counter++; ClassName = $"Arr{typeDef.Length}Special" + Counter++; } ReferenzName = $"{NamespaceName}.{ClassName}"; - var targetClass = new CodeTypeDeclaration(ClassName) - { - IsClass = true, - TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed - }; - targetClass.BaseTypes.Add(new CodeTypeReference("BaseType")); + ClassDeclarationSyntax targetClass = SyntaxFactory.ClassDeclaration(ClassName) + .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.SealedKeyword)) + .AddBaseListTypes(SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName("BaseType"))); + + //FieldDeclarationSyntax valueField = SyntaxFactory.FieldDeclaration(SyntaxFactory.VariableDeclaration(SyntaxFactory.ParseTypeName($"{fullItem}[]")) + // .AddVariables(SyntaxFactory.VariableDeclarator("_value"))) + // .AddModifiers(SyntaxFactory.Token(SyntaxKind.PrivateKeyword)); + //targetClass = targetClass.AddMembers(valueField); + + PropertyDeclarationSyntax valueProperty = SyntaxFactory.PropertyDeclaration(SyntaxFactory.ParseTypeName($"{fullItem}[]"), "Value") + .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) + .AddAccessorListAccessors( + SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)), + SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))); + targetClass = targetClass.AddMembers(valueProperty); + + PropertyDeclarationSyntax typeSizeProperty = SyntaxFactory.PropertyDeclaration(SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.IntKeyword)), "TypeSize") + .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.OverrideKeyword))) + .WithAccessorList(SyntaxFactory.AccessorList(SyntaxFactory.SingletonList( + SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration) + .WithBody(SyntaxFactory.Block( + SyntaxFactory.SingletonList( + SyntaxFactory.ReturnStatement(SyntaxFactory.LiteralExpression(SyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal((int)typeDef.Length))) + ) + )) + ))); + targetClass = targetClass.AddMembers(typeSizeProperty); + + ReturnStatementSyntax typeNameReturn = SyntaxFactory.ReturnStatement( + SyntaxFactory.InvocationExpression( + SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.ParseTypeName("string"), + SyntaxFactory.IdentifierName("Format")), + SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(new[] + { + SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal("[{0}; {1}]"))), + SyntaxFactory.Argument( + SyntaxFactory.InvocationExpression( + SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.ObjectCreationExpression(SyntaxFactory.ParseTypeName(fullItem.ToString())).WithArgumentList(SyntaxFactory.ArgumentList()), + SyntaxFactory.IdentifierName("TypeName")))), + SyntaxFactory.Argument(SyntaxFactory.IdentifierName("TypeSize")) + })))); + // Declaring a name method + MethodDeclarationSyntax nameMethod = SyntaxFactory.MethodDeclaration(SyntaxFactory.ParseTypeName("string"), "TypeName") + .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.OverrideKeyword)) + .WithBody(SyntaxFactory.Block(typeNameReturn)); + targetClass = targetClass.AddMembers(nameMethod); + + targetClass = AddTargetClassCustomAttributesRoslyn(targetClass, typeDef); // add comment to class if exists - targetClass.Comments.AddRange(GetComments(typeDef.Docs, typeDef)); - AddTargetClassCustomAttributes(targetClass, typeDef); + targetClass = targetClass.WithLeadingTrivia(GetCommentsRoslyn(typeDef.Docs, typeDef)); - typeNamespace.Types.Add(targetClass); + targetClass = targetClass.AddMembers(GetEncodeRoslyn(), GetDecodeRoslyn(fullItem.ToString())); - // Declaring a name method - CodeMemberMethod nameMethod = new() - { - Attributes = MemberAttributes.Public | MemberAttributes.Override, - Name = "TypeName", - ReturnType = new CodeTypeReference(typeof(string)) - }; - - var methodRef1 = new CodeMethodReferenceExpression(new CodeObjectCreateExpression(fullItem.ToString(), Array.Empty()), "TypeName()"); - var methodRef2 = new CodeMethodReferenceExpression(new CodeThisReferenceExpression(), "TypeSize"); - - // Declaring a return statement for method ToString. - CodeMethodReturnStatement returnStatement = - new() - { - Expression = - new CodeMethodInvokeExpression( - new CodeTypeReferenceExpression("System.String"), "Format", - new CodePrimitiveExpression("[{0}; {1}]"), - methodRef1, methodRef2) - }; - nameMethod.Statements.Add(returnStatement); - targetClass.Members.Add(nameMethod); - - CodeMemberProperty sizeProperty = new() - { - Attributes = MemberAttributes.Public | MemberAttributes.Override, - Name = "TypeSize", - Type = new CodeTypeReference(typeof(int)) - }; - sizeProperty.GetStatements.Add(new CodeMethodReturnStatement(new CodePrimitiveExpression((int)typeDef.Length))); - targetClass.Members.Add(sizeProperty); + MethodDeclarationSyntax createMethod = SyntaxFactory.MethodDeclaration(SyntaxFactory.ParseTypeName("void"), "Create") + .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) + .AddParameterListParameters(SyntaxFactory.Parameter(SyntaxFactory.Identifier("array")).WithType(SyntaxFactory.ParseTypeName($"{fullItem}[]"))) + .WithBody(SyntaxFactory.Block( + SyntaxFactory.ExpressionStatement(SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, SyntaxFactory.IdentifierName("Value"), SyntaxFactory.IdentifierName("array"))), + SyntaxFactory.ExpressionStatement(SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, SyntaxFactory.IdentifierName("Bytes"), SyntaxFactory.InvocationExpression(SyntaxFactory.IdentifierName("Encode")))))); + targetClass = targetClass.AddMembers(createMethod); - CodeMemberMethod encodeMethod = ArrayBuilder.GetEncode(); - targetClass.Members.Add(encodeMethod); + NamespaceDeclarationSyntax namespaceDeclaration = SyntaxFactory + .NamespaceDeclaration(SyntaxFactory.ParseName(NamespaceName)) + .AddMembers(targetClass); - CodeMemberMethod decodeMethod = ArrayBuilder.GetDecode(fullItem.ToString()); - targetClass.Members.Add(decodeMethod); + CompilationUnitSyntax compilationUnit = SyntaxFactory.CompilationUnit() + .AddMembers(namespaceDeclaration); + TargetUnit = TargetUnit.AddMembers(compilationUnit.Members.ToArray()); - CodeMemberField valueField = new() - { - Attributes = MemberAttributes.Private, - Name = "_value", - Type = new CodeTypeReference($"{fullItem}[]") - }; - targetClass.Members.Add(valueField); - CodeMemberProperty valueProperty = new() - { - Attributes = MemberAttributes.Public | MemberAttributes.Final, - Name = "Value", - HasGet = true, - HasSet = true, - Type = new CodeTypeReference($"{fullItem}[]") - }; - valueProperty.GetStatements.Add(new CodeMethodReturnStatement( - new CodeFieldReferenceExpression( - new CodeThisReferenceExpression(), valueField.Name))); - valueProperty.SetStatements.Add(new CodeAssignStatement( - new CodeFieldReferenceExpression( - new CodeThisReferenceExpression(), valueField.Name), - new CodePropertySetValueReferenceExpression())); - - - CodeMemberMethod createMethod = new() - { - Attributes = MemberAttributes.Public | MemberAttributes.Final, - Name = "Create" - }; - createMethod.Parameters.Add(new() - { - Type = new CodeTypeReference($"{fullItem.ToString()}[]"), - Name = "array" - }); - createMethod.Statements.Add(new CodeSnippetExpression("Value = array")); - createMethod.Statements.Add(new CodeSnippetExpression("Bytes = Encode()")); - targetClass.Members.Add(createMethod); - - targetClass.Members.Add(valueProperty); return this; } } -} +} \ No newline at end of file diff --git a/Tools/Substrate.DotNet/Service/Node/Base/BuilderBase.cs b/Tools/Substrate.DotNet/Service/Node/Base/BuilderBase.cs index db7de4e..0b0c3bc 100644 --- a/Tools/Substrate.DotNet/Service/Node/Base/BuilderBase.cs +++ b/Tools/Substrate.DotNet/Service/Node/Base/BuilderBase.cs @@ -1,20 +1,23 @@ -using Substrate.NetApi.Model.Meta; -using System.CodeDom; -using System.CodeDom.Compiler; +using Microsoft.CodeAnalysis.CSharp.Syntax; using System.Collections.Generic; using System.IO; using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Substrate.NetApi.Model.Meta; +using System; +using Serilog; +using System.Net.NetworkInformation; namespace Substrate.DotNet.Service.Node.Base { - public abstract class BuilderBase { - public static readonly List Files = new(); + protected static readonly List Files = new(); public uint Id { get; } - - NodeTypeResolver Resolver { get; } + + protected NodeTypeResolver Resolver { get; } public bool Success { get; set; } @@ -28,91 +31,42 @@ public abstract class BuilderBase public string ProjectName { get; private set; } - public CodeNamespace ImportsNamespace { get; set; } - - public CodeCompileUnit TargetUnit { get; set; } + public CompilationUnitSyntax TargetUnit { get; set; } public abstract BuilderBase Create(); - public BuilderBase(string projectName, uint id, NodeTypeResolver resolver) + protected BuilderBase(string projectName, uint id, NodeTypeResolver resolver) { ProjectName = projectName; Id = id; Resolver = resolver; - ImportsNamespace = new() - { - Imports = { - new CodeNamespaceImport("Substrate.NetApi.Model.Types.Base"), - new CodeNamespaceImport("System.Collections.Generic") - } - }; - TargetUnit = new CodeCompileUnit(); - TargetUnit.Namespaces.Add(ImportsNamespace); - + TargetUnit = SyntaxFactory.CompilationUnit() + .AddUsings( + SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("Substrate.NetApi.Model.Types.Base")), + SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("System.Collections.Generic"))); Success = true; } - public NodeTypeResolved GetFullItemPath(uint typeId) + public static string EscapeIfKeyword(string parameterName) { - if (!Resolver.TypeNames.TryGetValue(typeId, out NodeTypeResolved fullItem)) - { - Success = false; - return null; - } - - return fullItem; - } - - public static CodeCommentStatementCollection GetComments(string[] docs, NodeType typeDef = null, - string typeName = null) - { - CodeCommentStatementCollection comments = new() - { - new CodeCommentStatement("", true) - }; - - if (typeDef != null) + if (SyntaxFacts.GetKeywordKind(parameterName) != SyntaxKind.None) { - string path = typeDef.Path != null ? "[" + string.Join('.', typeDef.Path) + "]" : ""; - comments.Add(new CodeCommentStatement($">> {typeDef.Id} - {typeDef.TypeDef}{path}", true)); - } - - if (typeName != null) - { - comments.Add(new CodeCommentStatement($">> {typeName}", true)); + // If it is a keyword, create a verbatim identifier which adds '@' prefix + parameterName = "@" + parameterName; } - if (docs != null) - { - foreach (string doc in docs) - { - comments.Add(new CodeCommentStatement(doc, true)); - } - } - - comments.Add(new CodeCommentStatement("", true)); - return comments; + return parameterName; } - public static CodeMemberMethod SimpleMethod(string name, string returnType = null, object returnExpression = null) + public NodeTypeResolved GetFullItemPath(uint typeId) { - CodeMemberMethod nameMethod = new() - { - Attributes = MemberAttributes.Public | MemberAttributes.Override, - Name = name - }; - - if (returnType != null) + if (!Resolver.TypeNames.TryGetValue(typeId, out NodeTypeResolved fullItem)) { - nameMethod.ReturnType = new CodeTypeReference(returnType); - CodeMethodReturnStatement nameReturnStatement = new() - { - Expression = new CodePrimitiveExpression(returnExpression) - }; - nameMethod.Statements.Add(nameReturnStatement); + Success = false; + return null; } - return nameMethod; + return fullItem; } public virtual void Build(bool write, out bool success, string basePath = null) @@ -120,30 +74,24 @@ public virtual void Build(bool write, out bool success, string basePath = null) success = Success; if (write && Success) { - var provider = CodeDomProvider.CreateProvider("CSharp"); - CodeGeneratorOptions options = new() - { - BracingStyle = "C" - }; - string path = GetPath(basePath); Directory.CreateDirectory(Path.GetDirectoryName(path)); - if (Files.Contains(path)) + if (!Files.Contains(path)) { - // TODO (svnscha) Why does this happen? - // Console.WriteLine($"Overwriting[BUG]: {path}"); - //path += _index++; + Files.Add(path); } else { - Files.Add(path); + Log.Warning($"Overwriting[BUG]: {path}"); } + // add autogenerated header + TargetUnit = TargetUnit.WithLeadingTrivia(SyntaxFactory.TriviaList(HeaderComment)); + using StreamWriter sourceWriter = new(path); - provider.GenerateCodeFromCompileUnit( - TargetUnit, sourceWriter, options); + sourceWriter.Write(TargetUnit.NormalizeWhitespace().ToFullString()); } } @@ -153,7 +101,7 @@ private string GetPath(string basePath) space.Add((FileName is null ? ClassName : FileName) + ".cs"); - // Remove the first two parts of the namespace to avoid the files being created in the Substrate/NetApi sub folder. + // Remove the first two parts of the namespace to avoid the files being created in the Substrate/NetApi sub folder. space = space.TakeLast(space.Count - 2).ToList(); // Add base path at the beginning of the paths list @@ -167,16 +115,83 @@ private string GetPath(string basePath) return path; } - protected void AddTargetClassCustomAttributes(CodeTypeDeclaration targetClass, NodeType typeDef) + protected ClassDeclarationSyntax AddTargetClassCustomAttributesRoslyn(ClassDeclarationSyntax targetClass, NodeType typeDef) { // TODO (svnscha): Change version to given metadata version. - ImportsNamespace.Imports.Add(new CodeNamespaceImport("Substrate.NetApi.Model.Types.Metadata.V14")); - ImportsNamespace.Imports.Add(new CodeNamespaceImport($"Substrate.NetApi.Attributes")); + TargetUnit = TargetUnit.AddUsings( + SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("Substrate.NetApi.Model.Types.Metadata.V14")), + SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("Substrate.NetApi.Attributes"))); - targetClass.CustomAttributes.Add(new CodeAttributeDeclaration(new CodeTypeReference("SubstrateNodeType"), new CodeAttributeArgument( - new CodeSnippetExpression($"TypeDefEnum.{typeDef.TypeDef}") - ))); + AttributeArgumentSyntax attributeArgument = SyntaxFactory.AttributeArgument( + SyntaxFactory.ParseExpression($"TypeDefEnum.{typeDef.TypeDef}")); + AttributeListSyntax attributeList = SyntaxFactory.AttributeList( + SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.Attribute(SyntaxFactory.IdentifierName("SubstrateNodeType"), SyntaxFactory.AttributeArgumentList(SyntaxFactory.SingletonSeparatedList(attributeArgument))))); + + return targetClass.AddAttributeLists(attributeList); } + + public static MethodDeclarationSyntax SimpleMethodRoslyn(string name, string returnType = null, object returnExpression = null) + { + MethodDeclarationSyntax nameMethod = SyntaxFactory.MethodDeclaration(SyntaxFactory.ParseTypeName(returnType ?? "void"), name) + .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.OverrideKeyword)); + + if (returnType != null && returnExpression != null) + { + nameMethod = nameMethod.WithBody(SyntaxFactory.Block( + SyntaxFactory.ReturnStatement(SyntaxFactory.ParseExpression($"\"{returnExpression}\"")) + )); + } + + return nameMethod; + } + + public static SyntaxTriviaList GetCommentsRoslyn(string[] docs, NodeType typeDef = null, string typeName = null) + { + var commentList = new List + { + SyntaxFactory.Comment("/// ") + }; + + if (typeDef != null) + { + string path = typeDef.Path != null ? "[" + string.Join('.', typeDef.Path) + "]" : ""; + commentList.Add(SyntaxFactory.Comment($"/// >> {typeDef.Id} - {typeDef.TypeDef}{path}")); + } + + if (typeName != null) + { + commentList.Add(SyntaxFactory.Comment($"/// >> {typeName}")); + } + + if (docs != null) + { + foreach (string doc in docs) + { + string[] lines = doc.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None); + + foreach (string line in lines) + { + commentList.Add(SyntaxFactory.Comment($"/// {line}")); + } + } + } + + commentList.Add(SyntaxFactory.Comment("/// ")); + + return SyntaxFactory.TriviaList(commentList); + } + + public static SyntaxTrivia HeaderComment => SyntaxFactory.Comment( +@"//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------"); + } } \ No newline at end of file diff --git a/Tools/Substrate.DotNet/Service/Node/Base/ClientBuilderBase.cs b/Tools/Substrate.DotNet/Service/Node/Base/ClientBuilderBase.cs index 199996e..fea0922 100644 --- a/Tools/Substrate.DotNet/Service/Node/Base/ClientBuilderBase.cs +++ b/Tools/Substrate.DotNet/Service/Node/Base/ClientBuilderBase.cs @@ -1,4 +1,6 @@ -using System.CodeDom; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System.CodeDom; using System.Collections.Generic; namespace Substrate.DotNet.Service.Node.Base @@ -7,12 +9,13 @@ public abstract class ClientBuilderBase : BuilderBase { public List ModuleNames { get; } - public ClientBuilderBase(string projectName, uint id, List moduleNames, NodeTypeResolver typeDict) + protected ClientBuilderBase(string projectName, uint id, List moduleNames, NodeTypeResolver typeDict) : base(projectName, id, typeDict) { ModuleNames = moduleNames; - NamespaceName = $"{ProjectName}.Generated"; - ImportsNamespace.Imports.Add(new CodeNamespaceImport($"{ProjectName}.Generated.Storage")); + + TargetUnit = TargetUnit.AddUsings( + SyntaxFactory.UsingDirective(SyntaxFactory.ParseName($"{ProjectName}.Generated.Storage"))); } } } \ No newline at end of file diff --git a/Tools/Substrate.DotNet/Service/Node/Base/ModuleBuilderBase.cs b/Tools/Substrate.DotNet/Service/Node/Base/ModuleBuilderBase.cs index 9edc26f..8e61eb2 100644 --- a/Tools/Substrate.DotNet/Service/Node/Base/ModuleBuilderBase.cs +++ b/Tools/Substrate.DotNet/Service/Node/Base/ModuleBuilderBase.cs @@ -12,7 +12,7 @@ public abstract class ModuleBuilderBase : BuilderBase public string PrefixName { get; private set; } - public ModuleBuilderBase(string projectName, uint id, PalletModule module, NodeTypeResolver typeDict, + protected ModuleBuilderBase(string projectName, uint id, PalletModule module, NodeTypeResolver typeDict, Dictionary nodeTypes) : base(projectName, id, typeDict) { diff --git a/Tools/Substrate.DotNet/Service/Node/Base/ModulesBuilderBase.cs b/Tools/Substrate.DotNet/Service/Node/Base/ModulesBuilderBase.cs deleted file mode 100644 index 1e28b55..0000000 --- a/Tools/Substrate.DotNet/Service/Node/Base/ModulesBuilderBase.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Substrate.NetApi.Model.Meta; -using System.Collections.Generic; - -namespace Substrate.DotNet.Service.Node.Base -{ - public abstract class ModulesBuilderBase : BuilderBase - { - public Dictionary NodeTypes { get; private set; } - - public PalletModule[] Modules { get; private set; } - - public ModulesBuilderBase(string projectName, uint id, PalletModule[] modules, NodeTypeResolver typeDict, - Dictionary nodeTypes) - : base(projectName, id, typeDict) - { - NodeTypes = nodeTypes; - Modules = modules; - } - } -} \ No newline at end of file diff --git a/Tools/Substrate.DotNet/Service/Node/Base/TypeBuilderBase.cs b/Tools/Substrate.DotNet/Service/Node/Base/TypeBuilderBase.cs index 341478d..85a0b0b 100644 --- a/Tools/Substrate.DotNet/Service/Node/Base/TypeBuilderBase.cs +++ b/Tools/Substrate.DotNet/Service/Node/Base/TypeBuilderBase.cs @@ -1,8 +1,4 @@ -using Substrate.DotNet.Extensions; -using Substrate.NetApi.Model.Meta; -using System.Collections.Generic; -using System.Linq; -using System.Text.RegularExpressions; +using Substrate.NetApi.Model.Meta; namespace Substrate.DotNet.Service.Node.Base { diff --git a/Tools/Substrate.DotNet/Service/Node/ClientBuilder.cs b/Tools/Substrate.DotNet/Service/Node/ClientBuilder.cs index d8d0a30..4682938 100644 --- a/Tools/Substrate.DotNet/Service/Node/ClientBuilder.cs +++ b/Tools/Substrate.DotNet/Service/Node/ClientBuilder.cs @@ -1,11 +1,8 @@ -using Substrate.DotNet.Service.Node.Base; -using Substrate.NetApi; -using Substrate.NetApi.Model.Extrinsics; -using Substrate.NetApi.Model.Meta; -using System; -using System.CodeDom; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Substrate.DotNet.Service.Node.Base; using System.Collections.Generic; -using System.Reflection; namespace Substrate.DotNet.Service.Node { @@ -26,91 +23,99 @@ public override ClientBuilder Create() ClassName = "SubstrateClientExt"; NamespaceName = $"{ProjectName}.Generated"; - CodeNamespace typeNamespace = new(NamespaceName); - TargetUnit.Namespaces.Add(typeNamespace); - - ImportsNamespace.Imports.Add(new CodeNamespaceImport("Substrate.NetApi.Model.Meta")); - ImportsNamespace.Imports.Add(new CodeNamespaceImport("Substrate.NetApi.Model.Extrinsics")); - - var targetClass = new CodeTypeDeclaration(ClassName) - { - IsClass = true, - TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed - }; - targetClass.BaseTypes.Add(new CodeTypeReference(typeof(SubstrateClient))); - typeNamespace.Types.Add(targetClass); - - CodeConstructor constructor = new() - { - Attributes = MemberAttributes.Public | MemberAttributes.Final - }; - - // Add parameters. - constructor.Parameters.Add( - new CodeParameterDeclarationExpression(typeof(Uri), "uri")); - constructor.Parameters.Add( - new CodeParameterDeclarationExpression(typeof(ChargeType), "chargeType")); - - constructor.BaseConstructorArgs.Add(new CodeVariableReferenceExpression("uri")); - constructor.BaseConstructorArgs.Add(new CodeVariableReferenceExpression("chargeType")); - - targetClass.Members.Add(constructor); - - CodeMemberField storageKeyField = new() - { - Attributes = MemberAttributes.Public, - Name = "StorageKeyDict", - Type = new CodeTypeReference(typeof(Dictionary, Tuple>)), - }; - storageKeyField.Comments.AddRange(GetComments(new string[] { $"{storageKeyField.Name} for key definition informations." }, null, null)); - targetClass.Members.Add(storageKeyField); - - constructor.Statements.Add( - new CodeAssignStatement( - new CodeVariableReferenceExpression(storageKeyField.Name), - new CodeObjectCreateExpression(storageKeyField.Type, Array.Empty()))); - - //CodeMemberField eventKeyField = new() - //{ - // Attributes = MemberAttributes.Public | MemberAttributes.Static, - // Name = "EventKeyDict", - // Type = new CodeTypeReference(typeof(Dictionary, Type>)), - //}; - //eventKeyField.Comments.AddRange(GetComments(new string[] { $"{eventKeyField.Name} for event definition informations." }, null, null)); - //targetClass.Members.Add(eventKeyField); - - //constructor.Statements.Add( - // new CodeAssignStatement( - // new CodeVariableReferenceExpression(eventKeyField.Name), - // new CodeObjectCreateExpression(eventKeyField.Type, new CodeExpression[] { }))); - + NamespaceDeclarationSyntax namespaceDeclaration = SyntaxFactory.NamespaceDeclaration(SyntaxFactory.ParseName(NamespaceName)); + + // Using Directives + namespaceDeclaration = namespaceDeclaration.AddUsings( + SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("Substrate.NetApi.Model.Meta")), + SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("Substrate.NetApi.Model.Extrinsics")) + ); + + ClassDeclarationSyntax targetClass = SyntaxFactory.ClassDeclaration(ClassName) + .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.SealedKeyword)) + .WithBaseList( + SyntaxFactory.BaseList().AddTypes(SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName("Substrate.NetApi.SubstrateClient"))) + ); + + // Constructor + ConstructorDeclarationSyntax constructor = SyntaxFactory.ConstructorDeclaration(ClassName) + .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) + .AddParameterListParameters( + SyntaxFactory.Parameter(SyntaxFactory.Identifier("uri")).WithType(SyntaxFactory.ParseTypeName("System.Uri")), + SyntaxFactory.Parameter(SyntaxFactory.Identifier("chargeType")).WithType(SyntaxFactory.ParseTypeName("ChargeType")) + ) + .WithInitializer( + SyntaxFactory.ConstructorInitializer(SyntaxKind.BaseConstructorInitializer) + .AddArgumentListArguments( + SyntaxFactory.Argument(SyntaxFactory.IdentifierName("uri")), + SyntaxFactory.Argument(SyntaxFactory.IdentifierName("chargeType")) + ) + ); + + // Field declaration + FieldDeclarationSyntax storageKeyField = SyntaxFactory.FieldDeclaration( + SyntaxFactory.VariableDeclaration( + SyntaxFactory.ParseTypeName("Dictionary, System.Tuple>")) + .AddVariables(SyntaxFactory.VariableDeclarator(SyntaxFactory.Identifier("StorageKeyDict")))) + .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) + .WithLeadingTrivia(GetCommentsRoslyn(new string[] { $"StorageKeyDict for key definition informations." }, null)); + targetClass = targetClass.AddMembers(storageKeyField); + + // Initialize field in constructor + constructor = constructor.WithBody(SyntaxFactory.Block( + SyntaxFactory.ExpressionStatement( + SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, + SyntaxFactory.IdentifierName("StorageKeyDict"), + SyntaxFactory.ObjectCreationExpression( + SyntaxFactory.ParseTypeName("Dictionary, System.Tuple>")) + .AddArgumentListArguments())))); + + // Module related logic foreach (string moduleName in ModuleNames) { string[] pallets = new string[] { "Storage" }; // , "Call"}; foreach (string pallet in pallets) { - CodeMemberField clientField = new() - { - Attributes = MemberAttributes.Public, - Name = moduleName, - Type = new CodeTypeReference(moduleName) - }; - clientField.Comments.AddRange(GetComments(new string[] { $"{moduleName} storage calls." }, null, null)); - targetClass.Members.Add(clientField); - - CodeFieldReferenceExpression fieldReference = - new(new CodeThisReferenceExpression(), moduleName); - - var createPallet = new CodeObjectCreateExpression(moduleName); - createPallet.Parameters.Add(new CodeThisReferenceExpression()); - constructor.Statements.Add(new CodeAssignStatement(fieldReference, createPallet)); + + // Property declaration + PropertyDeclarationSyntax moduleProperty = SyntaxFactory.PropertyDeclaration( + SyntaxFactory.ParseTypeName(moduleName), + SyntaxFactory.Identifier(moduleName)) + .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) + .AddAccessorListAccessors( + SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)) + ) + .WithLeadingTrivia(GetCommentsRoslyn(new string[] { $"{moduleName} storage calls." }, null)); + + + // Adding Field to Class + targetClass = targetClass.AddMembers(moduleProperty); + + // Initialize field in constructor + ExpressionStatementSyntax fieldAssignment = SyntaxFactory.ExpressionStatement( + SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, + SyntaxFactory.IdentifierName(moduleName), + SyntaxFactory.ObjectCreationExpression(SyntaxFactory.ParseTypeName(moduleName)) + .AddArgumentListArguments(SyntaxFactory.Argument(SyntaxFactory.ThisExpression())))); + + // Adding statement to constructor's body + constructor = constructor.AddBodyStatements(fieldAssignment); } } + // Reassigning the updated constructor to the class + targetClass = targetClass.RemoveNode(constructor, SyntaxRemoveOptions.KeepNoTrivia); + targetClass = targetClass.AddMembers(constructor); + + // Adding Class to Namespace + namespaceDeclaration = namespaceDeclaration.RemoveNode(targetClass, SyntaxRemoveOptions.KeepNoTrivia); + namespaceDeclaration = namespaceDeclaration.AddMembers(targetClass); + + // Adding Namespace to Compilation Unit + TargetUnit = TargetUnit.AddMembers(namespaceDeclaration); + return this; } - } - -} +} \ No newline at end of file diff --git a/Tools/Substrate.DotNet/Service/Node/EnumBuilder.cs b/Tools/Substrate.DotNet/Service/Node/EnumBuilder.cs index 4fbcbd5..a912d6b 100644 --- a/Tools/Substrate.DotNet/Service/Node/EnumBuilder.cs +++ b/Tools/Substrate.DotNet/Service/Node/EnumBuilder.cs @@ -1,12 +1,16 @@ -using Substrate.DotNet.Service.Node.Base; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp; +using Substrate.DotNet.Service.Node.Base; using Substrate.NetApi.Model.Meta; using Substrate.NetApi.Model.Types; using System; using System.CodeDom; -using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Reflection; +using System.Collections.Generic; +using Microsoft.CodeAnalysis; +using static Substrate.NetApi.Model.Meta.Storage; +using Serilog; namespace Substrate.DotNet.Service.Node { @@ -25,259 +29,99 @@ public static EnumBuilder Init(string projectName, uint id, NodeTypeVariant type public override TypeBuilderBase Create() { var typeDef = TypeDef as NodeTypeVariant; - string enumName = $"{typeDef.Path.Last()}"; - ClassName = $"Enum{enumName}"; + if (!Resolver.TypeNames.TryGetValue(Id, out NodeTypeResolved nodeTypeResolved)) + { + throw new NotSupportedException($"Could not find type {Id}"); + } + + ClassName = nodeTypeResolved.ClassName; + ReferenzName = $"{NamespaceName}.{ClassName}"; - CodeNamespace typeNamespace = new(NamespaceName); - TargetUnit.Namespaces.Add(typeNamespace); - CodeTypeDeclaration TargetType = new(enumName) - { - IsEnum = true - }; + NamespaceDeclarationSyntax typeNamespace = SyntaxFactory.NamespaceDeclaration(SyntaxFactory.ParseName(NamespaceName)); - if (typeDef.Variants != null) + // only add the enumeration in the first variations. + if (string.IsNullOrEmpty(nodeTypeResolved.Name.ClassNameSufix) || nodeTypeResolved.Name.ClassNameSufix == "1") { - foreach (TypeVariant variant in typeDef.Variants) - { - TargetType.Members.Add(new CodeMemberField(ClassName, variant.Name) + EnumDeclarationSyntax targetType = SyntaxFactory + .EnumDeclaration(enumName) + .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)); + + if (typeDef.Variants != null) { - InitExpression = new CodePrimitiveExpression(variant.Index) - }); - } + foreach (TypeVariant variant in typeDef.Variants) + { + targetType = targetType.AddMembers( + SyntaxFactory.EnumMemberDeclaration(EscapeIfKeyword(variant.Name)) + .WithEqualsValue(SyntaxFactory.EqualsValueClause(SyntaxFactory.LiteralExpression(SyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal(variant.Index)))) + ); + } + } + typeNamespace = typeNamespace.AddMembers(targetType); } - typeNamespace.Types.Add(TargetType); - var targetClass = new CodeTypeDeclaration(ClassName) - { - IsClass = true, - TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed - }; - targetClass.Comments.AddRange(GetComments(typeDef.Docs, typeDef)); + + ClassDeclarationSyntax targetClass = SyntaxFactory.ClassDeclaration(ClassName) + .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.SealedKeyword)); + targetClass = targetClass.WithLeadingTrivia(GetCommentsRoslyn(typeDef.Docs, typeDef)); if (typeDef.Variants == null || typeDef.Variants.All(p => p.TypeFields == null)) { - targetClass.BaseTypes.Add(new CodeTypeReference($"BaseEnum<{enumName}>")); - typeNamespace.Types.Add(targetClass); + targetClass = targetClass.AddBaseListTypes(SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName($"BaseEnum<{enumName}>"))); + typeNamespace = typeNamespace.AddMembers(targetClass); } else { - var codeTypeRef = new CodeTypeReference("BaseEnumExt"); - codeTypeRef.TypeArguments.Add(new CodeTypeReference(enumName)); + var genericTypeArguments = new List { SyntaxFactory.ParseTypeName(enumName) }; + int highIndex = typeDef.Variants.Max(p => p.Index); if (highIndex < 256) { for (int i = 0; i < highIndex + 1; i++) { - TypeVariant variant = typeDef.Variants.Where(p => p.Index == i).FirstOrDefault(); - //TypeVariant variant = typeDef.Variants[i]; + TypeVariant variant = typeDef.Variants.FirstOrDefault(p => p.Index == i); + if (variant == null || variant.TypeFields == null) { // add void type - codeTypeRef.TypeArguments.Add(new CodeTypeReference("BaseVoid")); + genericTypeArguments.Add(SyntaxFactory.ParseTypeName("BaseVoid")); } else { if (variant.TypeFields.Length == 1) { NodeTypeResolved item = GetFullItemPath(variant.TypeFields[0].TypeId); - codeTypeRef.TypeArguments.Add(new CodeTypeReference(item.ToString())); + genericTypeArguments.Add(SyntaxFactory.ParseTypeName(item.ToString())); } else { - var baseTuple = new CodeTypeReference("BaseTuple"); - + var baseTupleArgs = new List(); foreach (NodeTypeField field in variant.TypeFields) { NodeTypeResolved item = GetFullItemPath(field.TypeId); - baseTuple.TypeArguments.Add(new CodeTypeReference(item.ToString())); + baseTupleArgs.Add(SyntaxFactory.ParseTypeName(item.ToString())); } - codeTypeRef.TypeArguments.Add(baseTuple); + GenericNameSyntax baseTuple = SyntaxFactory.GenericName(SyntaxFactory.Identifier("BaseTuple"), SyntaxFactory.TypeArgumentList(SyntaxFactory.SeparatedList(baseTupleArgs))); + genericTypeArguments.Add(baseTuple); } } } } - // Unhandled enumerations are manually done else { - codeTypeRef.TypeArguments.Add(new CodeTypeReference("BaseVoid")); - - switch (enumName) - { - case "Era": - targetClass.Members.AddRange(GetEnumEra()); - break; - //case "Data": - // targetClass.Members.AddRange(GetEnumData()); - // break; - // TODO (svnscha): Why is this not supported yet? - //case "Event": - //case "DispatchError": - //case "Call": - // break; - default: - throw new NotImplementedException("Enum extension can't handle such big sized typed rust enumeration, please create a manual fix for it."); - } + throw new NotImplementedException("Enum extension can't handle such big sized typed rust enumeration, please create a manual fix for it."); } - targetClass.BaseTypes.Add(codeTypeRef); - typeNamespace.Types.Add(targetClass); + GenericNameSyntax baseEnumExt = SyntaxFactory.GenericName(SyntaxFactory.Identifier("BaseEnumExt"), SyntaxFactory.TypeArgumentList(SyntaxFactory.SeparatedList(genericTypeArguments))); + targetClass = targetClass.AddBaseListTypes(SyntaxFactory.SimpleBaseType(baseEnumExt)); + typeNamespace = typeNamespace.AddMembers(targetClass); } - return this; - } - private CodeTypeMemberCollection GetEnumEra() - { - - ImportsNamespace.Imports.Add(new CodeNamespaceImport("Substrate.NetApi.Model.Types")); - ImportsNamespace.Imports.Add(new CodeNamespaceImport("Substrate.NetApi.Model.Types.Primitive")); - - var result = new CodeTypeMemberCollection(); - - CodeMemberMethod decodeMethod = SimpleMethod("Decode"); - decodeMethod.Parameters.Add(new() - { - Type = new CodeTypeReference("System.Byte[]"), - Name = "byteArray" - }); - decodeMethod.Parameters.Add(new() - { - Type = new CodeTypeReference("System.Int32"), - Name = "p", - Direction = FieldDirection.Ref - }); - decodeMethod.Statements.Add(new CodeSnippetExpression("var start = p")); - decodeMethod.Statements.Add(new CodeSnippetExpression("var enumByte = byteArray[p]")); - decodeMethod.Statements.Add(new CodeSnippetExpression("Value = (Era)System.Enum.Parse(typeof(Era), enumByte.ToString(), true)")); - decodeMethod.Statements.Add(new CodeSnippetExpression("p += 1")); - decodeMethod.Statements.Add(new CodeSnippetExpression("Value2 = DecodeOneOf(enumByte, byteArray, ref p)")); - decodeMethod.Statements.Add(new CodeSnippetExpression("TypeSize = p - start")); - result.Add(decodeMethod); - - CodeMemberMethod decodeOneOfMethod = SimpleMethod("DecodeOneOf"); - decodeOneOfMethod.Attributes = MemberAttributes.Private; - decodeOneOfMethod.ReturnType = new CodeTypeReference(typeof(IType)); - decodeOneOfMethod.Parameters.Add(new() - { - Type = new CodeTypeReference("System.Byte"), - Name = "value" - }); - decodeOneOfMethod.Parameters.Add(new() - { - Type = new CodeTypeReference("System.Byte[]"), - Name = "byteArray" - }); - decodeOneOfMethod.Parameters.Add(new() - { - Type = new CodeTypeReference("System.Int32"), - Name = "p", - Direction = FieldDirection.Ref - }); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("IType result")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("if (value == 0) { return null; }")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("result = new U8()")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("result.Decode(byteArray, ref p)")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("return result")); - result.Add(decodeOneOfMethod); - - return result; - } + TargetUnit = TargetUnit.AddMembers(typeNamespace); - private CodeTypeMemberCollection GetEnumData() - { - ImportsNamespace.Imports.Add(new CodeNamespaceImport($"System")); - ImportsNamespace.Imports.Add(new CodeNamespaceImport("Substrate.NetApi.Model.Types")); - ImportsNamespace.Imports.Add(new CodeNamespaceImport("Substrate.NetApi.Model.Types.Primitive")); - ImportsNamespace.Imports.Add(new CodeNamespaceImport($"{ProjectName}.Generated.Types.Base")); - - var result = new CodeTypeMemberCollection(); - - CodeMemberMethod decodeMethod = SimpleMethod("Decode"); - decodeMethod.Parameters.Add(new() - { - Type = new CodeTypeReference("System.Byte[]"), - Name = "byteArray" - }); - decodeMethod.Parameters.Add(new() - { - Type = new CodeTypeReference("System.Int32"), - Name = "p", - Direction = FieldDirection.Ref - }); - decodeMethod.Statements.Add(new CodeSnippetExpression("var start = p")); - decodeMethod.Statements.Add(new CodeSnippetExpression("var enumByte = byteArray[p]")); - decodeMethod.Statements.Add(new CodeSnippetExpression("Value = (Data)System.Enum.Parse(typeof(Data), enumByte.ToString(), true)")); - decodeMethod.Statements.Add(new CodeSnippetExpression("p += 1")); - decodeMethod.Statements.Add(new CodeSnippetExpression("Value2 = DecodeOneOf(enumByte, byteArray, ref p)")); - decodeMethod.Statements.Add(new CodeSnippetExpression("TypeSize = p - start")); - decodeMethod.Statements.Add(new CodeSnippetExpression("Bytes = new byte[TypeSize]")); - decodeMethod.Statements.Add(new CodeSnippetExpression("Array.Copy(byteArray, start, base.Bytes, 0, TypeSize)")); - - result.Add(decodeMethod); - - CodeMemberMethod decodeOneOfMethod = SimpleMethod("DecodeOneOf"); - decodeOneOfMethod.Attributes = MemberAttributes.Private; - decodeOneOfMethod.ReturnType = new CodeTypeReference(typeof(IType)); - decodeOneOfMethod.Parameters.Add(new() - { - Type = new CodeTypeReference("System.Byte"), - Name = "value" - }); - decodeOneOfMethod.Parameters.Add(new() - { - Type = new CodeTypeReference("System.Byte[]"), - Name = "byteArray" - }); - decodeOneOfMethod.Parameters.Add(new() - { - Type = new CodeTypeReference("System.Int32"), - Name = "p", - Direction = FieldDirection.Ref - }); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("IType result")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("if (value == 0) { return new BaseVoid(); }")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("if (value == 1) { result = new Arr0U8(); result.Decode(byteArray, ref p); return result; }")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("if (value == 2) { result = new Arr1U8(); result.Decode(byteArray, ref p); return result; }")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("if (value == 3) { result = new Arr2U8(); result.Decode(byteArray, ref p); return result; }")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("if (value == 4) { result = new Arr3U8(); result.Decode(byteArray, ref p); return result; }")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("if (value == 5) { result = new Arr4U8(); result.Decode(byteArray, ref p); return result; }")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("if (value == 6) { result = new Arr5U8(); result.Decode(byteArray, ref p); return result; }")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("if (value == 7) { result = new Arr6U8(); result.Decode(byteArray, ref p); return result; }")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("if (value == 8) { result = new Arr7U8(); result.Decode(byteArray, ref p); return result; }")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("if (value == 9) { result = new Arr8U8(); result.Decode(byteArray, ref p); return result; }")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("if (value == 10) { result = new Arr9U8(); result.Decode(byteArray, ref p); return result; }")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("if (value == 11) { result = new Arr10U8(); result.Decode(byteArray, ref p); return result; }")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("if (value == 12) { result = new Arr11U8(); result.Decode(byteArray, ref p); return result; }")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("if (value == 13) { result = new Arr12U8(); result.Decode(byteArray, ref p); return result; }")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("if (value == 15) { result = new Arr14U8(); result.Decode(byteArray, ref p); return result; }")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("if (value == 16) { result = new Arr15U8(); result.Decode(byteArray, ref p); return result; }")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("if (value == 17) { result = new Arr16U8(); result.Decode(byteArray, ref p); return result; }")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("if (value == 18) { result = new Arr17U8(); result.Decode(byteArray, ref p); return result; }")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("if (value == 19) { result = new Arr18U8(); result.Decode(byteArray, ref p); return result; }")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("if (value == 20) { result = new Arr19U8(); result.Decode(byteArray, ref p); return result; }")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("if (value == 21) { result = new Arr20U8(); result.Decode(byteArray, ref p); return result; }")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("if (value == 22) { result = new Arr21U8(); result.Decode(byteArray, ref p); return result; }")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("if (value == 23) { result = new Arr22U8(); result.Decode(byteArray, ref p); return result; }")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("if (value == 24) { result = new Arr23U8(); result.Decode(byteArray, ref p); return result; }")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("if (value == 25) { result = new Arr24U8(); result.Decode(byteArray, ref p); return result; }")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("if (value == 26) { result = new Arr25U8(); result.Decode(byteArray, ref p); return result; }")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("if (value == 27) { result = new Arr26U8(); result.Decode(byteArray, ref p); return result; }")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("if (value == 28) { result = new Arr27U8(); result.Decode(byteArray, ref p); return result; }")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("if (value == 29) { result = new Arr28U8(); result.Decode(byteArray, ref p); return result; }")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("if (value == 30) { result = new Arr29U8(); result.Decode(byteArray, ref p); return result; }")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("if (value == 31) { result = new Arr30U8(); result.Decode(byteArray, ref p); return result; }")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("if (value == 32) { result = new Arr31U8(); result.Decode(byteArray, ref p); return result; }")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("if (value == 33) { result = new Arr32U8(); result.Decode(byteArray, ref p); return result; }")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("if (value == 34) { result = new Arr32U8(); result.Decode(byteArray, ref p); return result; }")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("if (value == 35) { result = new Arr32U8(); result.Decode(byteArray, ref p); return result; }")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("if (value == 36) { result = new Arr32U8(); result.Decode(byteArray, ref p); return result; }")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("if (value == 37) { result = new Arr32U8(); result.Decode(byteArray, ref p); return result; }")); - decodeOneOfMethod.Statements.Add(new CodeSnippetExpression("throw new NotImplementedException(\"Invalid leading byte, please check source\");")); - result.Add(decodeOneOfMethod); - - return result; + return this; } } -} +} \ No newline at end of file diff --git a/Tools/Substrate.DotNet/Service/Node/EventModuleBuilder.cs b/Tools/Substrate.DotNet/Service/Node/EventModuleBuilder.cs deleted file mode 100644 index 8a55774..0000000 --- a/Tools/Substrate.DotNet/Service/Node/EventModuleBuilder.cs +++ /dev/null @@ -1,107 +0,0 @@ -using Substrate.DotNet.Extensions; -using Substrate.DotNet.Service.Node.Base; -using Substrate.NetApi.Model.Meta; -using System; -using System.CodeDom; -using System.Collections.Generic; -using System.Reflection; - -namespace Substrate.DotNet.Service.Node -{ - public class EventModuleBuilder : ModulesBuilderBase - { - private EventModuleBuilder(string projectName, uint id, PalletModule[] modules, NodeTypeResolver typeDict, Dictionary nodeTypes) : - base(projectName, id, modules, typeDict, nodeTypes) - { - } - - public static EventModuleBuilder Init(string projectName, uint id, PalletModule[] modules, NodeTypeResolver typeDict, Dictionary nodeTypes) - { - return new EventModuleBuilder(projectName, id, modules, typeDict, nodeTypes); - } - - public override EventModuleBuilder Create() - { - ImportsNamespace.Imports.Add(new CodeNamespaceImport("System.Threading.Tasks")); - ImportsNamespace.Imports.Add(new CodeNamespaceImport("Substrate.NetApi.Model.Meta")); - ImportsNamespace.Imports.Add(new CodeNamespaceImport("System.Threading")); - ImportsNamespace.Imports.Add(new CodeNamespaceImport("Substrate.NetApi.Model.Types")); - ImportsNamespace.Imports.Add(new CodeNamespaceImport("Substrate.NetApi.Model.Extrinsics")); - - FileName = "NodeEvents"; - NamespaceName = "Substrate.NetApi.Model.Event"; - ReferenzName = "Substrate.NetApi.Model.Event"; - - CodeNamespace typeNamespace = new(NamespaceName); - TargetUnit.Namespaces.Add(typeNamespace); - - // add constructor - CodeConstructor constructor = new() - { - Attributes = - MemberAttributes.Public | MemberAttributes.Final - }; - - foreach (PalletModule module in Modules) - { - PalletEvents events = module.Events; - - if (events != null) - { - if (NodeTypes.TryGetValue(events.TypeId, out NodeType nodeType)) - { - var typeDef = nodeType as NodeTypeVariant; - - foreach (TypeVariant variant in typeDef.Variants) - { - var eventClass = new CodeTypeDeclaration("Event" + variant.Name.MakeMethod()) - { - IsClass = true, - TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed - }; - - // add comment to variant if exists - eventClass.Comments.AddRange(GetComments(variant.Docs, null, variant.Name)); - - var codeTypeRef = new CodeTypeReference("BaseTuple"); - if (variant.TypeFields != null) - { - foreach (NodeTypeField field in variant.TypeFields) - { - NodeTypeResolved fullItem = GetFullItemPath(field.TypeId); - codeTypeRef.TypeArguments.Add(new CodeTypeReference(fullItem.ToString())); - } - } - eventClass.BaseTypes.Add(codeTypeRef); - - // add event key mapping in constructor - constructor.Statements.Add( - EventModuleBuilder.AddPropertyValues(new CodeExpression[] { - new CodeObjectCreateExpression( - new CodeTypeReference(typeof(Tuple)), - new CodeExpression[] { - new CodePrimitiveExpression((int) module.Index), - new CodePrimitiveExpression( variant.Index) - }), - new CodeTypeOfExpression(ReferenzName + "." + eventClass.Name) - - }, "_client.EventKeyDict")); - - typeNamespace.Types.Add(eventClass); - } - } - } - } - return this; - } - - private static CodeStatement AddPropertyValues(CodeExpression[] exprs, string variableReference) - { - return new CodeExpressionStatement( - new CodeMethodInvokeExpression( - new CodeMethodReferenceExpression( - new CodeTypeReferenceExpression( - new CodeTypeReference(variableReference)), "Add"), exprs)); - } - } -} diff --git a/Tools/Substrate.DotNet/Service/Node/GetMetadata.cs b/Tools/Substrate.DotNet/Service/Node/GetMetadata.cs index c2cb6de..98881f2 100644 --- a/Tools/Substrate.DotNet/Service/Node/GetMetadata.cs +++ b/Tools/Substrate.DotNet/Service/Node/GetMetadata.cs @@ -1,10 +1,10 @@ #nullable enable +using Serilog; using Substrate.NetApi; using Substrate.NetApi.Model.Extrinsics; using Substrate.NetApi.Model.Meta; using Substrate.NetApi.Model.Types.Metadata; -using Serilog; using System; using System.IO; using System.Threading; @@ -98,4 +98,4 @@ internal static string GetRuntimeFromFile(ILogger logger, string serviceArgument return null; } } -} +} \ No newline at end of file diff --git a/Tools/Substrate.DotNet/Service/Node/ModuleGenBuilder.cs b/Tools/Substrate.DotNet/Service/Node/ModuleGenBuilder.cs index 59d860d..5e125f8 100644 --- a/Tools/Substrate.DotNet/Service/Node/ModuleGenBuilder.cs +++ b/Tools/Substrate.DotNet/Service/Node/ModuleGenBuilder.cs @@ -1,13 +1,13 @@ -using Substrate.DotNet.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Substrate.DotNet.Extensions; using Substrate.DotNet.Service.Node.Base; using Substrate.NetApi.Model.Extrinsics; using Substrate.NetApi.Model.Meta; -using Substrate.NetApi.Model.Types; using System; -using System.CodeDom; using System.Collections.Generic; using System.Linq; -using System.Reflection; namespace Substrate.DotNet.Service.Node { @@ -25,214 +25,202 @@ public static ModuleGenBuilder Init(string projectName, uint id, PalletModule mo public override ModuleGenBuilder Create() { - ImportsNamespace.Imports.Add(new CodeNamespaceImport("System.Threading.Tasks")); - ImportsNamespace.Imports.Add(new CodeNamespaceImport($"Substrate.NetApi.Model.Meta")); - ImportsNamespace.Imports.Add(new CodeNamespaceImport("System.Threading")); - ImportsNamespace.Imports.Add(new CodeNamespaceImport($"Substrate.NetApi")); - ImportsNamespace.Imports.Add(new CodeNamespaceImport($"Substrate.NetApi.Model.Types")); - ImportsNamespace.Imports.Add(new CodeNamespaceImport($"Substrate.NetApi.Model.Extrinsics")); + UsingDirectiveSyntax[] usings = new[] + { + "System.Threading.Tasks", + "Substrate.NetApi.Model.Meta", + "System.Threading", + "Substrate.NetApi", + "Substrate.NetApi.Model.Types", + "Substrate.NetApi.Model.Extrinsics", + }.Select(u => SyntaxFactory.UsingDirective(SyntaxFactory.ParseName(u))).ToArray(); FileName = "Main" + Module.Name; NamespaceName = $"{ProjectName}.Generated.Storage"; ReferenzName = NamespaceName; - CodeNamespace typeNamespace = new(NamespaceName); - TargetUnit.Namespaces.Add(typeNamespace); + NamespaceDeclarationSyntax typeNamespace = SyntaxFactory + .NamespaceDeclaration(SyntaxFactory.ParseName(NamespaceName)); - // add constructor - CodeConstructor constructor = new() - { - Attributes = MemberAttributes.Public | MemberAttributes.Final - }; + typeNamespace = CreateStorage(typeNamespace); + typeNamespace = CreateCalls(typeNamespace); + typeNamespace = CreateEvents(typeNamespace); + typeNamespace = CreateConstants(typeNamespace); + typeNamespace = CreateErrors(typeNamespace); - CreateStorage(typeNamespace, constructor); - CreateCalls(typeNamespace); - CreateEvents(typeNamespace); - CreateConstants(typeNamespace); - CreateErrors(typeNamespace); + TargetUnit = TargetUnit + .AddUsings(usings) + .AddMembers(typeNamespace); return this; } - private void CreateStorage(CodeNamespace typeNamespace, CodeConstructor constructor) + private NamespaceDeclarationSyntax CreateStorage(NamespaceDeclarationSyntax typeNamespace) { ClassName = Module.Name + "Storage"; PalletStorage storage = Module.Storage; - var targetClass = new CodeTypeDeclaration(ClassName) - { - IsClass = true, - TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed - }; - typeNamespace.Types.Add(targetClass); - - // Declare the client field. - var clientField = new CodeMemberField - { - Attributes = MemberAttributes.Private, - Name = "_client", - Type = new CodeTypeReference("SubstrateClientExt") - }; - clientField.Comments.Add(new CodeCommentStatement("Substrate client for the storage calls.")); - targetClass.Members.Add(clientField); + ConstructorDeclarationSyntax constructor = SyntaxFactory + .ConstructorDeclaration(ClassName) + .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) + .WithBody(SyntaxFactory.Block()); + + // Create the class + ClassDeclarationSyntax targetClass = SyntaxFactory.ClassDeclaration(ClassName) + .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.SealedKeyword))); + + // Create the client field. + FieldDeclarationSyntax clientField = SyntaxFactory.FieldDeclaration( + SyntaxFactory.VariableDeclaration( + SyntaxFactory.ParseTypeName("SubstrateClientExt")) + .WithVariables( + SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.VariableDeclarator(SyntaxFactory.Identifier("_client"))))) + .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PrivateKeyword))) + .WithLeadingTrivia(GetCommentsRoslyn(new string[] { "Substrate client for the storage calls." })); + targetClass = targetClass.AddMembers(clientField); // Add parameters. - constructor.Parameters.Add(new CodeParameterDeclarationExpression( - clientField.Type, "client")); - CodeFieldReferenceExpression fieldReference = - new(new CodeThisReferenceExpression(), "_client"); - constructor.Statements.Add(new CodeAssignStatement(fieldReference, - new CodeArgumentReferenceExpression("client"))); - - targetClass.Members.Add(constructor); + constructor = constructor.AddParameterListParameters( + SyntaxFactory.Parameter(SyntaxFactory.Identifier("client")) + .WithType(SyntaxFactory.ParseTypeName(clientField.Declaration.Type.ToString()))); + + // Assignment statement for the constructor. + constructor = constructor.AddBodyStatements( + SyntaxFactory.ExpressionStatement( + SyntaxFactory.AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + SyntaxFactory.IdentifierName("_client"), + SyntaxFactory.IdentifierName("client")))); if (storage?.Entries != null) { foreach (Entry entry in storage.Entries) { string storageParams = entry.Name + "Params"; - CodeMemberMethod parameterMethod = new() - { - Attributes = MemberAttributes.Static | MemberAttributes.Public | MemberAttributes.Final, - Name = storageParams, - ReturnType = new CodeTypeReference(typeof(string)) - }; - // add comment to class if exists - parameterMethod.Comments.AddRange(GetComments(entry.Docs, null, storageParams)); - targetClass.Members.Add(parameterMethod); - - // default function - if (entry.Default != null || entry.Default.Length != 0) - { - string storageDefault = entry.Name + "Default"; - CodeMemberMethod defaultMethod = new() - { - Attributes = MemberAttributes.Static | MemberAttributes.Public | MemberAttributes.Final, - Name = storageDefault, - ReturnType = new CodeTypeReference(typeof(string)) - }; - // add comment to class if exists - defaultMethod.Comments.AddRange(GetComments(new string[] { "Default value as hex string" }, null, storageDefault)); - targetClass.Members.Add(defaultMethod); - // add return statement - defaultMethod.Statements.Add(new CodeMethodReturnStatement( - new CodePrimitiveExpression("0x" + BitConverter.ToString(entry.Default).Replace("-", string.Empty)))); - } - // async Task - CodeMemberMethod storageMethod = new() - { - Attributes = MemberAttributes.Public | MemberAttributes.Final, - Name = entry.Name, - }; - // add comment to class if exists - storageMethod.Comments.AddRange(GetComments(entry.Docs, null, entry.Name)); + // Create static method + MethodDeclarationSyntax parameterMethod = SyntaxFactory + .MethodDeclaration(SyntaxFactory.ParseTypeName("string"), storageParams) + .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword))) + .WithLeadingTrivia(GetCommentsRoslyn(entry.Docs, null, storageParams)); // Assuming GetComments() returns a string - targetClass.Members.Add(storageMethod); + MethodDeclarationSyntax storageMethod; + ExpressionStatementSyntax methodInvoke; + NodeTypeResolved returnValueStr; if (entry.StorageType == Storage.Type.Plain) { - NodeTypeResolved fullItem = GetFullItemPath(entry.TypeMap.Item1); - - parameterMethod.Statements.Add(new CodeMethodReturnStatement( - ModuleGenBuilder.GetStorageString(storage.Prefix, entry.Name, entry.StorageType))); - - storageMethod.ReturnType = new CodeTypeReference($"async Task<{fullItem}>"); + returnValueStr = GetFullItemPath(entry.TypeMap.Item1); - storageMethod.Parameters.Add(new CodeParameterDeclarationExpression("CancellationToken", "token")); + storageMethod = SyntaxFactory + .MethodDeclaration(SyntaxFactory.ParseTypeName($"Task<{returnValueStr}>"), entry.Name); - CodeMethodInvokeExpression methodInvoke = new( - new CodeTypeReferenceExpression(targetClass.Name), - parameterMethod.Name, Array.Empty()); + parameterMethod = parameterMethod.AddBodyStatements( + SyntaxFactory.ReturnStatement(GetStorageStringRoslyn(storage.Prefix, entry.Name, entry.StorageType))); - CodeVariableDeclarationStatement variableDeclaration1 = new(typeof(string), "parameters", methodInvoke); - storageMethod.Statements.Add(variableDeclaration1); - - // create result - var resultStatement = new CodeArgumentReferenceExpression(ModuleGenBuilder.GetInvoceString(fullItem.ToString())); - CodeVariableDeclarationStatement variableDeclaration2 = new("var", "result", resultStatement); - storageMethod.Statements.Add(variableDeclaration2); - - // return statement - storageMethod.Statements.Add( - new CodeMethodReturnStatement( - new CodeVariableReferenceExpression("result"))); + methodInvoke = SyntaxFactory.ExpressionStatement(SyntaxFactory.InvocationExpression(SyntaxFactory.IdentifierName(parameterMethod.Identifier))); // add storage key mapping in constructor - constructor.Statements.Add( - ModuleGenBuilder.AddPropertyValues(ModuleGenBuilder.GetStorageMapString("", fullItem.ToString(), storage.Prefix, entry.Name), "_client.StorageKeyDict")); + constructor = constructor + .AddBodyStatements(AddPropertyValuesRoslyn(GetStorageMapStringRoslyn("", returnValueStr.ToString(), storage.Prefix, entry.Name), "_client.StorageKeyDict")); } else if (entry.StorageType == Storage.Type.Map) { TypeMap typeMap = entry.TypeMap.Item2; Storage.Hasher[] hashers = typeMap.Hashers; NodeTypeResolved key = GetFullItemPath(typeMap.Key); - NodeTypeResolved value = GetFullItemPath(typeMap.Value); - - parameterMethod.Parameters.Add(new CodeParameterDeclarationExpression(key.ToString(), "key")); - parameterMethod.Statements.Add(new CodeMethodReturnStatement( - ModuleGenBuilder.GetStorageString(storage.Prefix, entry.Name, entry.StorageType, hashers))); - - storageMethod.ReturnType = new CodeTypeReference($"async Task<{value}>"); - storageMethod.Parameters.Add(new CodeParameterDeclarationExpression(key.ToString(), "key")); - storageMethod.Parameters.Add(new CodeParameterDeclarationExpression("CancellationToken", "token")); - - CodeMethodInvokeExpression methodInvoke = new(new CodeTypeReferenceExpression(targetClass.Name), parameterMethod.Name, - new CodeExpression[] { new CodeArgumentReferenceExpression("key") }); - CodeVariableDeclarationStatement variableDeclaration = new(typeof(string), "parameters", methodInvoke); - storageMethod.Statements.Add(variableDeclaration); - - // create result - var resultStatement = new CodeArgumentReferenceExpression(ModuleGenBuilder.GetInvoceString(value.ToString())); - CodeVariableDeclarationStatement variableDeclaration2 = new("var", "result", resultStatement); - storageMethod.Statements.Add(variableDeclaration2); - - // default handling - //if (entry.Default != null || entry.Default.Length != 0) - //{ - // var conditionalStatement = new CodeConditionStatement( - // new CodeBinaryOperatorExpression( - // new CodeVariableReferenceExpression("result"), - // CodeBinaryOperatorType.ValueEquality, - // new CodePrimitiveExpression(null)), - // new CodeStatement[] { - // new CodeAssignStatement(new CodeVariableReferenceExpression("result"), new CodeObjectCreateExpression( value.ToString(), Array.Empty() )), - // new CodeExpressionStatement( - // new CodeMethodInvokeExpression( - // new CodeVariableReferenceExpression("result"), "Create", - // new CodeExpression[] { new CodePrimitiveExpression("0x" + BitConverter.ToString(entry.Default).Replace("-", string.Empty)) }))}); - // storageMethod.Statements.Add(conditionalStatement); - //} + returnValueStr = GetFullItemPath(typeMap.Value); - // return statement - storageMethod.Statements.Add( - new CodeMethodReturnStatement( - new CodeVariableReferenceExpression("result"))); + storageMethod = SyntaxFactory + .MethodDeclaration(SyntaxFactory.ParseTypeName($"Task<{returnValueStr}>"), entry.Name); + + parameterMethod = parameterMethod + .AddParameterListParameters(SyntaxFactory.Parameter(SyntaxFactory.Identifier("key")) + .WithType(SyntaxFactory.ParseTypeName(key.ToString()))) + .AddBodyStatements(SyntaxFactory.ReturnStatement(GetStorageStringRoslyn(storage.Prefix, entry.Name, entry.StorageType, hashers))); + + storageMethod = storageMethod + .AddParameterListParameters(SyntaxFactory.Parameter(SyntaxFactory.Identifier("key")) + .WithType(SyntaxFactory.ParseTypeName(key.ToString()))); + + ArgumentListSyntax argumentList = SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(new[] { SyntaxFactory.Argument(SyntaxFactory.IdentifierName("key")) })); + + methodInvoke = SyntaxFactory.ExpressionStatement( + SyntaxFactory.InvocationExpression(SyntaxFactory.IdentifierName(parameterMethod.Identifier), argumentList)); // add storage key mapping in constructor - constructor.Statements.Add(ModuleGenBuilder.AddPropertyValues(ModuleGenBuilder.GetStorageMapString(key.ToString(), value.ToString(), storage.Prefix, entry.Name, hashers), "_client.StorageKeyDict")); + constructor = constructor.AddBodyStatements(AddPropertyValuesRoslyn(GetStorageMapStringRoslyn(key.ToString(), returnValueStr.ToString(), storage.Prefix, entry.Name, hashers), "_client.StorageKeyDict")); } else { throw new NotImplementedException(); } + + storageMethod = storageMethod + .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.AsyncKeyword))) + .WithLeadingTrivia(GetCommentsRoslyn(entry.Docs, null, entry.Name)); // Assuming GetComments() returns a string + + storageMethod = storageMethod + .AddParameterListParameters(SyntaxFactory.Parameter(SyntaxFactory.Identifier("token")).WithType(SyntaxFactory.ParseTypeName("CancellationToken"))); + + VariableDeclarationSyntax variableDeclaration1 = SyntaxFactory.VariableDeclaration(SyntaxFactory.ParseTypeName("string")) + .AddVariables(SyntaxFactory.VariableDeclarator(SyntaxFactory.Identifier("parameters"), null, SyntaxFactory.EqualsValueClause(methodInvoke.Expression))); + + storageMethod = storageMethod.AddBodyStatements(SyntaxFactory.LocalDeclarationStatement(variableDeclaration1)); + + string resultString = GetInvoceString(returnValueStr.ToString()); + + VariableDeclarationSyntax variableDeclaration2 = SyntaxFactory + .VariableDeclaration(SyntaxFactory.IdentifierName("var")) + .AddVariables(SyntaxFactory.VariableDeclarator(SyntaxFactory.Identifier("result"), null, SyntaxFactory.EqualsValueClause(SyntaxFactory.IdentifierName(resultString)))); + + storageMethod = storageMethod.AddBodyStatements(SyntaxFactory.LocalDeclarationStatement(variableDeclaration2)); + + storageMethod = storageMethod.AddBodyStatements(SyntaxFactory.ReturnStatement(SyntaxFactory.IdentifierName("result"))); + + // add parameter method to the class + targetClass = targetClass.AddMembers(parameterMethod); + + // default function + if (entry.Default != null && entry.Default.Length != 0) + { + string storageDefault = entry.Name + "Default"; + MethodDeclarationSyntax defaultMethod = SyntaxFactory.MethodDeclaration(SyntaxFactory.ParseTypeName("string"), storageDefault) + .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword))) + .WithLeadingTrivia(GetCommentsRoslyn(new string[] { "Default value as hex string" }, null, storageDefault)); + + // Add return statement + defaultMethod = defaultMethod.WithBody(SyntaxFactory.Block(SyntaxFactory.ReturnStatement( + SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal("0x" + BitConverter.ToString(entry.Default).Replace("-", string.Empty)))))); + + // add default method to the class + targetClass = targetClass.AddMembers(defaultMethod); + } + + // add storage method to the class + targetClass = targetClass.AddMembers(storageMethod); } } + + // add constructor to the class + targetClass = targetClass.AddMembers(constructor); + + // Add class to the namespace. + typeNamespace = typeNamespace.AddMembers(targetClass); + + return typeNamespace; } - private void CreateCalls(CodeNamespace typeNamespace) + private NamespaceDeclarationSyntax CreateCalls(NamespaceDeclarationSyntax namespaceDeclaration) { ClassName = Module.Name + "Calls"; PalletCalls calls = Module.Calls; - var targetClass = new CodeTypeDeclaration(ClassName) - { - IsClass = true, - TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed - }; - typeNamespace.Types.Add(targetClass); + ClassDeclarationSyntax targetClass = SyntaxFactory.ClassDeclaration(ClassName) + .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.SealedKeyword))); if (calls != null) { @@ -244,20 +232,26 @@ private void CreateCalls(CodeNamespace typeNamespace) { foreach (TypeVariant variant in typeDef.Variants) { - CodeMemberMethod callMethod = new() - { - Attributes = MemberAttributes.Static | MemberAttributes.Public | MemberAttributes.Final, - Name = variant.Name.MakeMethod(), - ReturnType = new CodeTypeReference(typeof(Method).Name) - }; + MethodDeclarationSyntax callMethod = SyntaxFactory + .MethodDeclaration(SyntaxFactory.ParseTypeName(nameof(Method)), variant.Name.MakeMethod()) + .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword))) + .WithBody(SyntaxFactory.Block()); // add comment to class if exists - callMethod.Comments.AddRange(GetComments(typeDef.Docs, null, variant.Name)); + callMethod = callMethod.WithLeadingTrivia(GetCommentsRoslyn(typeDef.Docs, null, variant.Name)); string byteArrayName = "byteArray"; - callMethod.Statements.Add(new CodeVariableDeclarationStatement( - typeof(List), byteArrayName, new CodeObjectCreateExpression("List", Array.Empty()))); + TypeSyntax byteListType = SyntaxFactory.ParseTypeName("List"); + + callMethod = callMethod.AddBodyStatements( + SyntaxFactory.LocalDeclarationStatement(SyntaxFactory.VariableDeclaration( + byteListType, + SyntaxFactory.SingletonSeparatedList(SyntaxFactory.VariableDeclarator( + SyntaxFactory.Identifier(byteArrayName), + null, + SyntaxFactory.EqualsValueClause(SyntaxFactory.ObjectCreationExpression(byteListType) + .WithArgumentList(SyntaxFactory.ArgumentList()))))))); if (variant.TypeFields != null) { @@ -265,151 +259,177 @@ private void CreateCalls(CodeNamespace typeNamespace) { NodeTypeResolved fullItem = GetFullItemPath(field.TypeId); - CodeParameterDeclarationExpression param = new() - { - Type = new CodeTypeReference(fullItem.ToString()), - Name = field.Name - }; - callMethod.Parameters.Add(param); - - callMethod.Statements.Add(new CodeMethodInvokeExpression( - new CodeVariableReferenceExpression(byteArrayName), "AddRange", new CodeMethodInvokeExpression( - new CodeVariableReferenceExpression(field.Name), "Encode"))); + // Adding '@' prefix to the parameter + string parameterName = EscapeIfKeyword(field.Name); + + callMethod = callMethod.AddParameterListParameters( + SyntaxFactory.Parameter(SyntaxFactory.Identifier(parameterName)) + .WithType(SyntaxFactory.ParseTypeName(fullItem.ToString()))); + + callMethod = callMethod.AddBodyStatements( + SyntaxFactory.ExpressionStatement( + SyntaxFactory.InvocationExpression( + SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.IdentifierName(byteArrayName), + SyntaxFactory.IdentifierName("AddRange")), + SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.Argument( + SyntaxFactory.InvocationExpression( + SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.IdentifierName(parameterName), + SyntaxFactory.IdentifierName("Encode"))))))))); } } - // return statment - var create = new CodeObjectCreateExpression(typeof(Method).Name, Array.Empty()); - create.Parameters.Add(new CodePrimitiveExpression((int)Module.Index)); - create.Parameters.Add(new CodePrimitiveExpression(Module.Name)); - create.Parameters.Add(new CodePrimitiveExpression(variant.Index)); - create.Parameters.Add(new CodePrimitiveExpression(variant.Name)); - create.Parameters.Add(new CodeMethodInvokeExpression(new CodeVariableReferenceExpression(byteArrayName), "ToArray")); - CodeMethodReturnStatement returnStatement = new() - { - Expression = create - }; - - callMethod.Statements.Add(returnStatement); - targetClass.Members.Add(callMethod); + // return statement + ObjectCreationExpressionSyntax create = SyntaxFactory.ObjectCreationExpression(SyntaxFactory.ParseTypeName(nameof(Method))) + .WithArgumentList( + SyntaxFactory.ArgumentList( + SyntaxFactory.SeparatedList( + new SyntaxNodeOrToken[] + { + SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal((int)Module.Index))), + SyntaxFactory.Token(SyntaxKind.CommaToken), + SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(Module.Name))), + SyntaxFactory.Token(SyntaxKind.CommaToken), + SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal(variant.Index))), + SyntaxFactory.Token(SyntaxKind.CommaToken), + SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(variant.Name))), + SyntaxFactory.Token(SyntaxKind.CommaToken), + SyntaxFactory.Argument( + SyntaxFactory.InvocationExpression( + SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.IdentifierName(byteArrayName), + SyntaxFactory.IdentifierName("ToArray")))), + }))); + + ReturnStatementSyntax returnStatement = SyntaxFactory.ReturnStatement(create); + + callMethod = callMethod.AddBodyStatements(returnStatement); + targetClass = targetClass.AddMembers(callMethod); } } } } + + namespaceDeclaration = namespaceDeclaration.AddMembers(targetClass); + return namespaceDeclaration; } - private void CreateEvents(CodeNamespace typeNamespace) + private NamespaceDeclarationSyntax CreateEvents(NamespaceDeclarationSyntax namespaceDeclaration) { ClassName = Module.Name + "Events"; PalletEvents events = Module.Events; - //if (events != null) + //if (events != null && NodeTypes.TryGetValue(events.TypeId, out NodeType nodeType)) //{ - // if (NodeTypes.TryGetValue(events.TypeId, out NodeType nodeType)) - // { - // var typeDef = nodeType as NodeTypeVariant; + // var typeDef = nodeType as NodeTypeVariant; - // if (typeDef.Variants != null) + // if (typeDef.Variants != null) + // { + // foreach (TypeVariant variant in typeDef.Variants) // { - // foreach (TypeVariant variant in typeDef.Variants) - // { - // var eventClass = new CodeTypeDeclaration("Event" + variant.Name.MakeMethod()) - // { - // IsClass = true, - // TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed - // }; - - // // add comment to variant if exists - // eventClass.Comments.AddRange(GetComments(variant.Docs, null, variant.Name)); + // string eventClassName = "Event" + variant.Name.MakeMethod(); + // ClassDeclarationSyntax eventClass = SyntaxFactory.ClassDeclaration(eventClassName) + // .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.SealedKeyword))) + // .WithLeadingTrivia(GetCommentsRoslyn(variant.Docs, null, variant.Name)); - // var codeTypeRef = new CodeTypeReference("BaseTuple"); - // if (variant.TypeFields != null) + // QualifiedNameSyntax baseTupleType = SyntaxFactory.QualifiedName(SyntaxFactory.IdentifierName("BaseTuple"), SyntaxFactory.IdentifierName(string.Empty)); + // if (variant.TypeFields != null) + // { + // foreach (NodeTypeField field in variant.TypeFields) // { - // foreach (NodeTypeField field in variant.TypeFields) - // { - // NodeTypeResolved fullItem = GetFullItemPath(field.TypeId); - // codeTypeRef.TypeArguments.Add(new CodeTypeReference(fullItem.ToString())); - // } + // NodeTypeResolved fullItem = GetFullItemPath(field.TypeId); + // baseTupleType = baseTupleType.WithRight(SyntaxFactory.IdentifierName(fullItem.ToString())); // } - // eventClass.BaseTypes.Add(codeTypeRef); - - // // add event key mapping in constructor - // // TODO (svnscha) What is with events? - // //Console.WriteLine($"case \"{Module.Index}-{variant.Index}\": return typeof({NamespaceName + "." + eventClass.Name});"); - // //constructor.Statements.Add( - // // AddPropertyValues(new CodeExpression[] { - // // new CodeObjectCreateExpression( - // // new CodeTypeReference(typeof(Tuple)), - // // new CodeExpression[] { - // // new CodePrimitiveExpression((int) Module.Index), - // // new CodePrimitiveExpression((int) variant.Index) - // // }), - // // new CodeTypeOfExpression(NameSpace + "." + eventClass.Name) - - // // }, "SubstrateClientExt.EventKeyDict")); - - // typeNamespace.Types.Add(eventClass); // } + // eventClass = eventClass.AddBaseListTypes(SyntaxFactory.SimpleBaseType(baseTupleType)); + + // namespaceDeclaration = namespaceDeclaration.AddMembers(eventClass); // } // } //} + + return namespaceDeclaration; } - private void CreateConstants(CodeNamespace typeNamespace) + private NamespaceDeclarationSyntax CreateConstants(NamespaceDeclarationSyntax namespaceDeclaration) { ClassName = Module.Name + "Constants"; PalletConstant[] constants = Module.Constants; - var targetClass = new CodeTypeDeclaration(ClassName) - { - IsClass = true, - TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed - }; - typeNamespace.Types.Add(targetClass); + ClassDeclarationSyntax targetClass = SyntaxFactory.ClassDeclaration(ClassName) + .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.SealedKeyword))); if (constants != null && constants.Any()) { foreach (PalletConstant constant in constants) { - // async Task - CodeMemberMethod constantMethod = new() - { - Attributes = MemberAttributes.Public | MemberAttributes.Final, - Name = constant.Name, - }; + MethodDeclarationSyntax constantMethod = SyntaxFactory.MethodDeclaration(SyntaxFactory.ParseTypeName("void"), constant.Name) + .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword))); + // add comment to class if exists - constantMethod.Comments.AddRange(GetComments(constant.Docs, null, constant.Name)); + constantMethod = constantMethod.WithLeadingTrivia(GetCommentsRoslyn(constant.Docs, null, constant.Name)); - targetClass.Members.Add(constantMethod); + targetClass = targetClass.AddMembers(constantMethod); if (NodeTypes.TryGetValue(constant.TypeId, out NodeType nodeType)) { NodeTypeResolved nodeTypeResolved = GetFullItemPath(nodeType.Id); - constantMethod.ReturnType = new CodeTypeReference(nodeTypeResolved.ToString()); + constantMethod = constantMethod.WithReturnType(SyntaxFactory.ParseTypeName(nodeTypeResolved.ToString())); // assign new result object - CodeVariableDeclarationStatement newStatement = new("var", "result", new CodeObjectCreateExpression(nodeTypeResolved.ToString(), Array.Empty())); - constantMethod.Statements.Add(newStatement); + constantMethod = constantMethod.AddBodyStatements( + SyntaxFactory.LocalDeclarationStatement( + SyntaxFactory.VariableDeclaration( + SyntaxFactory.IdentifierName("var"), + SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.VariableDeclarator( + SyntaxFactory.Identifier("result"), + null, + SyntaxFactory.EqualsValueClause( + SyntaxFactory.ObjectCreationExpression( + SyntaxFactory.ParseTypeName(nodeTypeResolved.ToString()), + SyntaxFactory.ArgumentList(), + null))))))); // create with hex string object - var createStatement = new CodeExpressionStatement( - new CodeMethodInvokeExpression( - new CodeVariableReferenceExpression("result"), "Create", - new CodeExpression[] { new CodePrimitiveExpression("0x" + BitConverter.ToString(constant.Value).Replace("-", string.Empty)) })); - constantMethod.Statements.Add(createStatement); + constantMethod = constantMethod.AddBodyStatements( + SyntaxFactory.ExpressionStatement( + SyntaxFactory.InvocationExpression( + SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.IdentifierName("result"), + SyntaxFactory.IdentifierName("Create")), + SyntaxFactory.ArgumentList( + SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.Argument( + SyntaxFactory.LiteralExpression( + SyntaxKind.StringLiteralExpression, + SyntaxFactory.Literal("0x" + BitConverter.ToString(constant.Value).Replace("-", string.Empty))))))))); // return statement - constantMethod.Statements.Add( - new CodeMethodReturnStatement( - new CodeVariableReferenceExpression("result"))); + constantMethod = constantMethod.AddBodyStatements( + SyntaxFactory.ReturnStatement( + SyntaxFactory.IdentifierName("result"))); + + targetClass = targetClass.ReplaceNode( + targetClass.DescendantNodes().OfType().Single(m => m.Identifier.Text == constant.Name), + constantMethod); } } } + + namespaceDeclaration = namespaceDeclaration.AddMembers(targetClass); + return namespaceDeclaration; } - private void CreateErrors(CodeNamespace typeNamespace) + private NamespaceDeclarationSyntax CreateErrors(NamespaceDeclarationSyntax namespaceDeclaration) { ClassName = Module.Name + "Errors"; @@ -421,28 +441,27 @@ private void CreateErrors(CodeNamespace typeNamespace) { var typeDef = nodeType as NodeTypeVariant; - var targetClass = new CodeTypeDeclaration(ClassName) - { - IsEnum = true, - TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed - }; + EnumDeclarationSyntax targetClass = SyntaxFactory.EnumDeclaration(ClassName) + .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword))); if (typeDef.Variants != null) { foreach (TypeVariant variant in typeDef.Variants) { - var enumField = new CodeMemberField(ClassName, variant.Name); + EnumMemberDeclarationSyntax enumField = SyntaxFactory.EnumMemberDeclaration(variant.Name); // add comment to field if exists - enumField.Comments.AddRange(GetComments(variant.Docs, null, variant.Name)); + enumField = enumField.WithLeadingTrivia(GetCommentsRoslyn(variant.Docs, null, variant.Name)); - targetClass.Members.Add(enumField); + targetClass = targetClass.AddMembers(enumField); } } - typeNamespace.Types.Add(targetClass); + namespaceDeclaration = namespaceDeclaration.AddMembers(targetClass); } } + + return namespaceDeclaration; } private static string GetInvoceString(string returnType) @@ -450,100 +469,144 @@ private static string GetInvoceString(string returnType) return "await _client.GetStorageAsync<" + returnType + ">(parameters, token)"; } - private static CodeMethodInvokeExpression GetStorageString(string module, string item, Storage.Type type, Storage.Hasher[] hashers = null) + private static InvocationExpressionSyntax GetStorageStringRoslyn(string module, string item, Storage.Type type, Storage.Hasher[] hashers = null) { - var codeExpressions = - new CodeExpression[] { - new CodePrimitiveExpression(module), - new CodePrimitiveExpression(item), - new CodePropertyReferenceExpression(new CodeTypeReferenceExpression(typeof(Storage.Type)), type.ToString()) + var codeExpressions = new List + { + SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(module))), + SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(item))), + SyntaxFactory.Argument( + SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.IdentifierName("Substrate.NetApi.Model.Meta.Storage.Type"), + SyntaxFactory.IdentifierName(type.ToString()))) }; // if it is a map fill hashers and key if (hashers != null && hashers.Length > 0) { - CodeExpression keyReference = new CodeArrayCreateExpression( - new CodeTypeReference(typeof(IType)), - new CodeArgumentReferenceExpression[] { - new CodeArgumentReferenceExpression("key") - }); + ExpressionSyntax keyReference = SyntaxFactory.ArrayCreationExpression( + SyntaxFactory.ArrayType( + SyntaxFactory.IdentifierName("Substrate.NetApi.Model.Types.IType"), + SyntaxFactory.SingletonList( + SyntaxFactory.ArrayRankSpecifier( + SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.OmittedArraySizeExpression())))), + SyntaxFactory.InitializerExpression(SyntaxKind.ArrayInitializerExpression, + SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.IdentifierName("key")))); if (hashers.Length > 1) { - keyReference = new CodeSnippetExpression("key.Value"); + keyReference = SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.IdentifierName("key"), + SyntaxFactory.IdentifierName("Value") + ); } - codeExpressions = new CodeExpression[] { - new CodePrimitiveExpression(module), - new CodePrimitiveExpression(item), - new CodePropertyReferenceExpression( - new CodeTypeReferenceExpression(typeof(Storage.Type)), type.ToString()), - new CodeArrayCreateExpression( - new CodeTypeReference(typeof(Storage.Hasher)), - hashers.Select(p => new CodePropertyReferenceExpression( - new CodeTypeReferenceExpression(typeof(Storage.Hasher)), p.ToString())).ToArray()), - keyReference - }; + codeExpressions = new List + { + SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(module))), + SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(item))), + SyntaxFactory.Argument( + SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.IdentifierName("Substrate.NetApi.Model.Meta.Storage.Type"), + SyntaxFactory.IdentifierName(type.ToString()))), + SyntaxFactory.Argument( + SyntaxFactory.ArrayCreationExpression( + SyntaxFactory.ArrayType( + SyntaxFactory.IdentifierName("Substrate.NetApi.Model.Meta.Storage.Hasher"), + SyntaxFactory.SingletonList(SyntaxFactory.ArrayRankSpecifier())), + SyntaxFactory.InitializerExpression(SyntaxKind.ArrayInitializerExpression, + SyntaxFactory.SeparatedList( + hashers.Select(p => SyntaxFactory.ParseExpression($"Substrate.NetApi.Model.Meta.Storage.Hasher.{p}")))))), + SyntaxFactory.Argument(keyReference) + }; } - return new CodeMethodInvokeExpression(new CodeTypeReferenceExpression("RequestGenerator"), "GetStorage", codeExpressions); + return SyntaxFactory.InvocationExpression( + SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.IdentifierName("RequestGenerator"), + SyntaxFactory.IdentifierName("GetStorage"))) + .WithArgumentList(SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(codeExpressions))); } - private static CodeExpression[] GetStorageMapString(string keyType, string returnType, string module, string item, Storage.Hasher[] hashers = null) + private static ExpressionSyntax[] GetStorageMapStringRoslyn(string keyType, string returnType, string module, string item, Storage.Hasher[] hashers = null) { - var typeofReturn = new CodeTypeOfExpression(returnType); - - var result = new CodeExpression[] { - new CodeObjectCreateExpression( - new CodeTypeReference(typeof(Tuple)), - new CodeExpression[] { - new CodePrimitiveExpression(module), - new CodePrimitiveExpression(item) - }), - new CodeObjectCreateExpression( - new CodeTypeReference(typeof(Tuple)), - new CodeExpression[] { - new CodePrimitiveExpression(null), - new CodePrimitiveExpression(null), - typeofReturn}) - }; + TypeOfExpressionSyntax typeofReturn = SyntaxFactory.TypeOfExpression(SyntaxFactory.IdentifierName(returnType)); + + var result = new ExpressionSyntax[] { + SyntaxFactory.ObjectCreationExpression( + SyntaxFactory.IdentifierName("System.Tuple"), + SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList( + new ArgumentSyntax[] + { + SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(module))), + SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(item))) + })), + null), + SyntaxFactory.ObjectCreationExpression( + SyntaxFactory.IdentifierName("System.Tuple"), + SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList( + new ArgumentSyntax[] + { + SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.NullLiteralExpression)), + SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.NullLiteralExpression)), + SyntaxFactory.Argument(typeofReturn) + })), + null) + }; // if it is a map fill hashers and key if (hashers != null && hashers.Length > 0) { - var arrayExpression = new CodeArrayCreateExpression( - new CodeTypeReference(typeof(Storage.Hasher)), - hashers.Select(p => new CodePropertyReferenceExpression( - new CodeTypeReferenceExpression(typeof(Storage.Hasher)), p.ToString())).ToArray()); - var typeofType = new CodeTypeOfExpression(keyType); - - result = new CodeExpression[] { - new CodeObjectCreateExpression( - new CodeTypeReference(typeof(Tuple)), - new CodeExpression[] { - new CodePrimitiveExpression(module), - new CodePrimitiveExpression(item) - }), - new CodeObjectCreateExpression( - new CodeTypeReference(typeof(Tuple)), - new CodeExpression[] { - arrayExpression, - typeofType, - typeofReturn - }) - }; + ArrayCreationExpressionSyntax arrayExpression = SyntaxFactory.ArrayCreationExpression( + SyntaxFactory.ArrayType(SyntaxFactory.IdentifierName("Substrate.NetApi.Model.Meta.Storage.Hasher[]")), + SyntaxFactory.InitializerExpression(SyntaxKind.ArrayInitializerExpression, + SyntaxFactory.SeparatedList(hashers.Select(p => + SyntaxFactory.ParseExpression($"Substrate.NetApi.Model.Meta.Storage.Hasher.{p}"))))); + + TypeOfExpressionSyntax typeofType = SyntaxFactory.TypeOfExpression(SyntaxFactory.IdentifierName(keyType)); + + result = + new ExpressionSyntax[] { + SyntaxFactory.ObjectCreationExpression( + SyntaxFactory.IdentifierName("System.Tuple"), + SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList( + new ArgumentSyntax[] + { + SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(module))), + SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(item))) + })), + null), + SyntaxFactory.ObjectCreationExpression( + SyntaxFactory.IdentifierName("System.Tuple"), + SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList( + new ArgumentSyntax[] + { + SyntaxFactory.Argument(arrayExpression), + SyntaxFactory.Argument(typeofType), + SyntaxFactory.Argument(typeofReturn) + })), + null) + }; } return result; } - private static CodeStatement AddPropertyValues(CodeExpression[] exprs, string variableReference) + private static ExpressionStatementSyntax AddPropertyValuesRoslyn(ExpressionSyntax[] exprs, string variableReference) { - return new CodeExpressionStatement( - new CodeMethodInvokeExpression( - new CodeMethodReferenceExpression( - new CodeTypeReferenceExpression( - new CodeTypeReference(variableReference)), "Add"), exprs)); + return SyntaxFactory.ExpressionStatement( + SyntaxFactory.InvocationExpression( + SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.IdentifierName(variableReference), + SyntaxFactory.IdentifierName("Add")), + SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(exprs.Select(SyntaxFactory.Argument))))); } } } \ No newline at end of file diff --git a/Tools/Substrate.DotNet/Service/Node/NodeTypeResolver.cs b/Tools/Substrate.DotNet/Service/Node/NodeTypeResolver.cs index 2f81005..e97f110 100644 --- a/Tools/Substrate.DotNet/Service/Node/NodeTypeResolver.cs +++ b/Tools/Substrate.DotNet/Service/Node/NodeTypeResolver.cs @@ -1,11 +1,12 @@ #nullable enable + +using Serilog; using Substrate.DotNet.Extensions; using Substrate.NetApi.Model.Meta; using Substrate.NetApi.Model.Types.Metadata.V14; using System; using System.Collections.Generic; using System.Linq; -using System.Text; using System.Text.RegularExpressions; namespace Substrate.DotNet.Service.Node @@ -39,8 +40,9 @@ public class NodeTypeName public NodeTypeResolver Resolver { get; private set; } public NodeTypeNamespaceSource NamespaceSource { get; private set; } private string BaseName { get; set; } - public string ClassName => $"{ClassNamePrefix}{BaseName.Split('.').Last()}"; + public string ClassName => $"{ClassNamePrefix}{BaseName.Split('.').Last()}{ClassNameSufix}"; public string ClassNamePrefix { get; private set; } + public string ClassNameSufix { get; set; } public NodeTypeName[]? Arguments { get; private set; } public string ClassNameWithModule @@ -65,15 +67,17 @@ public string Namespace string[]? paths = BaseName.Split('.').ToArray(); string[]? reduced = paths.Take(paths.Length - 1).ToArray(); string? result = string.Join('.', reduced); - + if (string.IsNullOrEmpty(result)) { switch (NamespaceSource) { case NodeTypeNamespaceSource.Primitive: return "Substrate.NetApi.Model.Types.Primitive"; + case NodeTypeNamespaceSource.Generated: return $"{Resolver.NetApiProjectName}.Generated.Types.Base"; + default: break; } @@ -87,9 +91,13 @@ public string Namespace } public static NodeTypeName Primitive(NodeTypeResolver resolver, string baseName) => new NodeTypeName(resolver, NodeTypeNamespaceSource.Primitive, baseName, null); + public static NodeTypeName Base(NodeTypeResolver resolver, string baseName) => new NodeTypeName(resolver, NodeTypeNamespaceSource.Base, baseName, null); + public static NodeTypeName Base(NodeTypeResolver resolver, string baseName, NodeTypeName[]? arguments) => new NodeTypeName(resolver, NodeTypeNamespaceSource.Base, baseName, arguments); + public static NodeTypeName Generated(NodeTypeResolver resolver, string baseName) => new NodeTypeName(resolver, NodeTypeNamespaceSource.Generated, baseName, null); + public static NodeTypeName Generated(NodeTypeResolver resolver, string baseName, NodeTypeName[]? arguments) => new NodeTypeName(resolver, NodeTypeNamespaceSource.Generated, baseName, arguments); internal static NodeTypeName Array(NodeTypeResolver nodeTypeResolver, NodeTypeName nodeTypeName, uint length) @@ -109,20 +117,12 @@ private NodeTypeName(NodeTypeResolver resolver, NodeTypeNamespaceSource namespac BaseName = baseName; Arguments = arguments; ClassNamePrefix = string.Empty; + ClassNameSufix = string.Empty; } public override string ToString() { - string baseQualified; - - if (string.IsNullOrEmpty(ClassNamePrefix)) - { - baseQualified = $"{Namespace}.{ClassName}"; - } - else - { - baseQualified = $"{Namespace}.{ClassName}"; - } + string baseQualified = $"{Namespace}.{ClassName}"; if (Arguments == null) { @@ -151,12 +151,39 @@ private Dictionary Resolve(Dictionary ty { var result = new Dictionary(); + var dupeCountDict = new Dictionary>(); + foreach (uint typeId in types.Keys) { NodeTypeName name = ResolveTypeName(typeId, types); result.Add(typeId, new NodeTypeResolved(types[typeId], name)); + + string key = name.ToString(); + if (!dupeCountDict.ContainsKey(key)) + { + dupeCountDict[key] = new List(); + } + dupeCountDict[key].Add(typeId); } + // TODO major issue to be handled properly in NodeTypeResolver + // adding an index ass classname suffix to avoid overwriting + //foreach (List entries in dupeCountDict.Values.Where(p => p.Count > 1)) + //{ + // int index = 0; + // foreach (uint typeId in entries) + // { + // index++; + // NodeTypeResolved nodeTypeResolved = result[typeId]; + // if (nodeTypeResolved.Name.NamespaceSource == NodeTypeNamespaceSource.Generated) + // { + // Log.Debug("Renaming to {typeName}{index}", nodeTypeResolved.Name, index); + // nodeTypeResolved.Name.ClassNameSufix = index.ToString(); + // result[typeId] = nodeTypeResolved; + // } + // } + //} + return result; } @@ -238,8 +265,10 @@ private NodeTypeName ResolveVariantType(NodeTypeVariant nodeTypeVariant, Diction { case "Option": return NodeTypeName.Base(this, "BaseOpt", new NodeTypeName[] { ResolveTypeName(nodeTypeVariant.Variants[1].TypeFields[0].TypeId, types) }); + case "Void": return NodeTypeName.Base(this, "BaseVoid"); + default: break; } @@ -276,7 +305,6 @@ private string ResolvePathInternal(string[] path, string prefix) return $"{ResolvePathInternal(previousElements, string.Empty)}.{prefix}{lastElement}"; } - private static void EnsureTypeIdsIsNotNull(uint[] typeIds) { if (typeIds == null || typeIds.Length == 0) @@ -324,4 +352,4 @@ private string TypeNameSpace(string[] path) return path[0].MakeMethod(); } } -} +} \ No newline at end of file diff --git a/Tools/Substrate.DotNet/Service/Node/RestServiceControllerModuleBuilder.cs b/Tools/Substrate.DotNet/Service/Node/RestServiceControllerModuleBuilder.cs index d03fd6b..3ab9dfb 100644 --- a/Tools/Substrate.DotNet/Service/Node/RestServiceControllerModuleBuilder.cs +++ b/Tools/Substrate.DotNet/Service/Node/RestServiceControllerModuleBuilder.cs @@ -1,10 +1,14 @@ -using Substrate.DotNet.Extensions; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis; +using Substrate.DotNet.Extensions; using Substrate.DotNet.Service.Node.Base; using Substrate.NetApi.Model.Meta; using System; using System.CodeDom; using System.Collections.Generic; using System.Reflection; +using System.Linq; namespace Substrate.DotNet.Service.Node { @@ -25,178 +29,217 @@ public static RestServiceControllerModuleBuilder Init(string projectName, string public override RestServiceControllerModuleBuilder Create() { - if (Module.Storage == null) { Success = false; return this; } - ImportsNamespace.Imports.Add(new CodeNamespaceImport($"{ProjectName}.Generated.Storage")); - ImportsNamespace.Imports.Add(new CodeNamespaceImport("Microsoft.AspNetCore.Mvc")); - ImportsNamespace.Imports.Add(new CodeNamespaceImport("System.Threading.Tasks")); - ImportsNamespace.Imports.Add(new CodeNamespaceImport("Substrate.ServiceLayer.Attributes")); FileName = Module.Storage.Prefix + "Controller"; ReferenzName = $"{ProjectName}.Generated.Controller.{FileName}"; NamespaceName = $"{ProjectName}.Generated.Controller"; - CodeNamespace typeNamespace = new(NamespaceName); - TargetUnit.Namespaces.Add(typeNamespace); + SyntaxList usingDirectives = new SyntaxList() + .Add(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName($"{ProjectName}.Generated.Storage"))) + .Add(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("Microsoft.AspNetCore.Mvc"))) + .Add(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("System.Threading.Tasks"))) + .Add(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("Substrate.ServiceLayer.Attributes"))); + + NamespaceDeclarationSyntax namespaceDeclaration = SyntaxFactory + .NamespaceDeclaration(SyntaxFactory.ParseName(NamespaceName)); + + CreateController(namespaceDeclaration); - CreateController(typeNamespace); + TargetUnit = TargetUnit.AddUsings(usingDirectives.ToArray()); return this; } - private void CreateController(CodeNamespace typeNamespace) + private void CreateController(NamespaceDeclarationSyntax namespaceDeclaration) { ClassName = FileName; - var targetClass = new CodeTypeDeclaration(ClassName) + // Create ControllerBase attribute + AttributeListSyntax baseAttribute = SyntaxFactory.AttributeList(SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.Attribute(SyntaxFactory.IdentifierName("ApiController()")))); + + // Create Route attribute + AttributeListSyntax routeAttribute = SyntaxFactory.AttributeList(SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.Attribute( + SyntaxFactory.IdentifierName("Route"), + SyntaxFactory.AttributeArgumentList(SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.AttributeArgument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal("[controller]")))))))); + + // Creating class declaration + ClassDeclarationSyntax targetClass = SyntaxFactory.ClassDeclaration(ClassName) + .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.SealedKeyword)) + .AddBaseListTypes(SyntaxFactory.SimpleBaseType(SyntaxFactory.IdentifierName("ControllerBase"))) + .AddAttributeLists(baseAttribute, routeAttribute) + .WithLeadingTrivia(GetCommentsRoslyn(new string[] { $"{ClassName} controller to access storages." })); + + // Assuming that fieldName and Module.Storage.Prefix are string variables + string fieldName = $"{Module.Storage.Prefix}Storage"; + string fieldNamePublic = char.ToLower(fieldName[0]) + fieldName.Substring(1); + string fieldNamePrivate = "_" + fieldNamePublic; + + // Field declaration + FieldDeclarationSyntax field = SyntaxFactory.FieldDeclaration( + SyntaxFactory.VariableDeclaration( + SyntaxFactory.IdentifierName($"I{fieldName}"), + SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.VariableDeclarator(SyntaxFactory.Identifier(fieldNamePrivate))))) + .AddModifiers(SyntaxFactory.Token(SyntaxKind.PrivateKeyword)); + + + targetClass = targetClass.AddMembers(field); + + // Create constructor + ConstructorDeclarationSyntax constructor = SyntaxFactory + .ConstructorDeclaration(SyntaxFactory.Identifier(ClassName)) + .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) + .WithLeadingTrivia(GetCommentsRoslyn(new string[] { $"{ClassName} constructor." })) + .WithParameterList( + SyntaxFactory.ParameterList( + SyntaxFactory.SeparatedList(new[] { + SyntaxFactory.Parameter(SyntaxFactory.Identifier(fieldNamePublic)) + .WithType(SyntaxFactory.ParseTypeName($"I{fieldName}")) + }))) + .WithBody( + SyntaxFactory.Block( + SyntaxFactory.ExpressionStatement( + SyntaxFactory.AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + SyntaxFactory.IdentifierName(fieldNamePrivate), + SyntaxFactory.IdentifierName(fieldNamePublic))))); + // Add constructor to class + targetClass = targetClass.AddMembers(constructor); + + + // Assuming you have a method that accepts a string and returns a MethodDeclarationSyntax object + // Iterate over the storage entries and create the methods + if (Module.Storage.Entries != null) { - IsClass = true, - TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed + foreach (Entry entry in Module.Storage.Entries) + { + // Assuming CreateMethod is a method that creates a MethodDeclarationSyntax object for a given entry + MethodDeclarationSyntax methodDeclaration = CreateMethod(fieldNamePrivate, entry); + methodDeclaration = methodDeclaration + .WithLeadingTrivia(GetCommentsRoslyn(entry.Docs, null, entry.Name)); - }; - targetClass.BaseTypes.Add(new CodeTypeReference("ControllerBase")); - targetClass.Comments.AddRange(GetComments(new string[] { $"{ClassName} controller to access storages." })); + targetClass = targetClass.AddMembers(methodDeclaration); + } + } + namespaceDeclaration = namespaceDeclaration.AddMembers(targetClass); - typeNamespace.Types.Add(targetClass); - targetClass.CustomAttributes.Add( - new CodeAttributeDeclaration("ApiController")); - targetClass.CustomAttributes.Add( - new CodeAttributeDeclaration("Route", - new CodeAttributeArgument[] { - new CodeAttributeArgument(new CodePrimitiveExpression("[controller]")) - })); + TargetUnit = TargetUnit.AddMembers(namespaceDeclaration); + } - string fieldName = $"{Module.Storage.Prefix}Storage"; - CodeMemberField field = new() + private MethodDeclarationSyntax CreateMethod(string fieldNamePrivate, Entry entry) + { + // Prepare the method parameters and return type based on the entry.StorageType + TypeSyntax baseReturnType; + var methodParameters = new List(); + + ExpressionSyntax invokeExpression; + + if (entry.StorageType == Storage.Type.Plain) + { + NodeTypeResolved fullItem = GetFullItemPath(entry.TypeMap.Item1); + baseReturnType = SyntaxFactory.ParseTypeName(fullItem.ToString()); + invokeExpression = SyntaxFactory.IdentifierName(""); + } + else if (entry.StorageType == Storage.Type.Map) { - Attributes = MemberAttributes.Private, - Name = fieldName.MakePrivateField(), - Type = new CodeTypeReference($"I{fieldName}") - }; - targetClass.Members.Add(field); + TypeMap typeMap = entry.TypeMap.Item2; + Storage.Hasher[] hashers = typeMap.Hashers; + NodeTypeResolved value = GetFullItemPath(typeMap.Value); + baseReturnType = SyntaxFactory.ParseTypeName(value.ToString()); + + methodParameters.Add(SyntaxFactory.Parameter(SyntaxFactory.Identifier("key")) + .WithType(SyntaxFactory.ParseTypeName("string"))); - CodeConstructor constructor = new() + invokeExpression = SyntaxFactory.IdentifierName("key"); + } + else { - Attributes = - MemberAttributes.Public | MemberAttributes.Final - }; - targetClass.Members.Add(constructor); - constructor.Comments.AddRange(GetComments(new string[] { $"{ClassName} constructor." })); + throw new NotImplementedException(); + } - constructor.Parameters.Add(new CodeParameterDeclarationExpression($"I{fieldName}", fieldName.MakePublicField())); + string indentifier = $"Get{entry.Name}"; - // constructor initialize storage properties - constructor.Statements.Add(new CodeAssignStatement( - new CodeVariableReferenceExpression(field.Name), - new CodeVariableReferenceExpression(fieldName.MakePublicField()))); + MethodDeclarationSyntax getStorageMethod = SyntaxFactory.MethodDeclaration(SyntaxFactory.ParseTypeName("IActionResult"), indentifier) + .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) + .WithBody(SyntaxFactory.Block( + SyntaxFactory.ReturnStatement( + SyntaxFactory.InvocationExpression( + SyntaxFactory.IdentifierName("Ok"), + SyntaxFactory.ArgumentList( + SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.Argument( + SyntaxFactory.InvocationExpression( + SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.IdentifierName(fieldNamePrivate), + SyntaxFactory.IdentifierName(indentifier)), + SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument(invokeExpression))))))))))); - if (Module.Storage.Entries != null) + + // Add parameters to method + foreach (ParameterSyntax parameter in methodParameters) { - foreach (Entry entry in Module.Storage.Entries) - { - CodeParameterDeclarationExpression parameterDeclaration; - CodeTypeReference baseReturnType; - CodeExpression[] codeExpressions; - if (entry.StorageType == Storage.Type.Plain) - { - NodeTypeResolved fullItem = GetFullItemPath(entry.TypeMap.Item1); - baseReturnType = new CodeTypeReference(fullItem.ToString()); - parameterDeclaration = null; - codeExpressions = Array.Empty(); - } - else if (entry.StorageType == Storage.Type.Map) - { - TypeMap typeMap = entry.TypeMap.Item2; - Storage.Hasher[] hashers = typeMap.Hashers; - NodeTypeResolved value = GetFullItemPath(typeMap.Value); - baseReturnType = new CodeTypeReference(value.ToString()); - parameterDeclaration = new CodeParameterDeclarationExpression(typeof(string), "key"); - codeExpressions = new CodeExpression[] { - new CodeVariableReferenceExpression(parameterDeclaration.Name) - }; - } - else - { - throw new NotImplementedException(); - } - - // create get and gets - CodeMemberMethod getStorageMethod = new() - { - Attributes = MemberAttributes.Public | MemberAttributes.Final, - Name = $"Get{entry.Name}", - ReturnType = new CodeTypeReference("IActionResult") - }; - getStorageMethod.Comments.AddRange(GetComments(entry.Docs, null, entry.Name)); - getStorageMethod.CustomAttributes.Add( - new CodeAttributeDeclaration("HttpGet", - new CodeAttributeArgument[] { - new CodeAttributeArgument(new CodePrimitiveExpression(entry.Name)) - }) - ); - getStorageMethod.CustomAttributes.Add( - new CodeAttributeDeclaration("ProducesResponseType", - new CodeAttributeArgument[] { - new CodeAttributeArgument(new CodeTypeOfExpression(baseReturnType)), - new CodeAttributeArgument(new CodePrimitiveExpression(200)) - })); - - if (entry.StorageType == Storage.Type.Plain) - { - string prefixName = Module.Name == "System" ? "Frame" : "Pallet"; - - getStorageMethod.CustomAttributes.Add( - new CodeAttributeDeclaration("StorageKeyBuilder", - new CodeAttributeArgument[] { - new CodeAttributeArgument(new CodeTypeOfExpression($"{NetApiProjectName}.Generated.Storage.{Module.Name}Storage")), - new CodeAttributeArgument(new CodePrimitiveExpression($"{entry.Name}Params")) - })); - } - else if (entry.StorageType == Storage.Type.Map) - { - string prefixName = Module.Name == "System" ? "Frame" : "Pallet"; - - getStorageMethod.CustomAttributes.Add( - new CodeAttributeDeclaration("StorageKeyBuilder", - new CodeAttributeArgument[] { - new CodeAttributeArgument(new CodeTypeOfExpression($"{NetApiProjectName}.Generated.Storage.{Module.Name}Storage")), - new CodeAttributeArgument(new CodePrimitiveExpression($"{entry.Name}Params")), - new CodeAttributeArgument(new CodeTypeOfExpression(GetFullItemPath(entry.TypeMap.Item2.Key).ToString())) - })); - } - else - { - throw new NotImplementedException(); - } - - - if (parameterDeclaration != null) - { - getStorageMethod.Parameters.Add(parameterDeclaration); - } - - getStorageMethod.Statements.Add( - new CodeMethodReturnStatement(new CodeMethodInvokeExpression( - new CodeThisReferenceExpression(), - "Ok", - new CodeExpression[] { new CodeMethodInvokeExpression( - new CodeVariableReferenceExpression(field.Name), - getStorageMethod.Name, - codeExpressions - )} - ))); - - targetClass.Members.Add(getStorageMethod); + getStorageMethod = getStorageMethod.AddParameterListParameters(parameter); + } + // Add HttpGet attribute + AttributeListSyntax getAttribute = SyntaxFactory.AttributeList(SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.Attribute(SyntaxFactory.IdentifierName("HttpGet"), + SyntaxFactory.AttributeArgumentList(SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.AttributeArgument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(entry.Name)))))))); + getStorageMethod = getStorageMethod.AddAttributeLists(getAttribute); + + // Add ProducesResponseType attribute + AttributeListSyntax responseAttribute = SyntaxFactory.AttributeList(SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.Attribute(SyntaxFactory.IdentifierName("ProducesResponseType"), + SyntaxFactory.AttributeArgumentList(SyntaxFactory.SeparatedList(new[] { + SyntaxFactory.AttributeArgument(SyntaxFactory.TypeOfExpression(baseReturnType)), + SyntaxFactory.AttributeArgument(SyntaxFactory.LiteralExpression(SyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal(200))) }))))); + getStorageMethod = getStorageMethod.AddAttributeLists(responseAttribute); + + // Add StorageKeyBuilder attribute + AttributeListSyntax storageAttribute; + + if (entry.StorageType == Storage.Type.Plain) + { + string prefixName = Module.Name == "System" ? "Frame" : "Pallet"; - } + storageAttribute = SyntaxFactory.AttributeList(SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.Attribute(SyntaxFactory.IdentifierName("StorageKeyBuilder"), + SyntaxFactory.AttributeArgumentList(SyntaxFactory.SeparatedList(new[] { + SyntaxFactory.AttributeArgument(SyntaxFactory.TypeOfExpression(SyntaxFactory.IdentifierName($"{NetApiProjectName}.Generated.Storage.{Module.Name}Storage"))), + SyntaxFactory.AttributeArgument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal($"{entry.Name}Params"))) }))))); + } + else if (entry.StorageType == Storage.Type.Map) + { + string prefixName = Module.Name == "System" ? "Frame" : "Pallet"; + + storageAttribute = SyntaxFactory.AttributeList(SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.Attribute(SyntaxFactory.IdentifierName("StorageKeyBuilder"), + SyntaxFactory.AttributeArgumentList(SyntaxFactory.SeparatedList(new[] { + SyntaxFactory.AttributeArgument(SyntaxFactory.TypeOfExpression(SyntaxFactory.IdentifierName($"{NetApiProjectName}.Generated.Storage.{Module.Name}Storage"))), + SyntaxFactory.AttributeArgument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal($"{entry.Name}Params"))), + SyntaxFactory.AttributeArgument(SyntaxFactory.TypeOfExpression(SyntaxFactory.IdentifierName(GetFullItemPath(entry.TypeMap.Item2.Key).ToString()))) }))))); + } + else + { + throw new NotImplementedException(); } + + getStorageMethod = getStorageMethod.AddAttributeLists(storageAttribute); + + return getStorageMethod; } + } -} +} \ No newline at end of file diff --git a/Tools/Substrate.DotNet/Service/Node/RestServiceStorageModuleBuilder.cs b/Tools/Substrate.DotNet/Service/Node/RestServiceStorageModuleBuilder.cs index ba1c252..1c0a427 100644 --- a/Tools/Substrate.DotNet/Service/Node/RestServiceStorageModuleBuilder.cs +++ b/Tools/Substrate.DotNet/Service/Node/RestServiceStorageModuleBuilder.cs @@ -1,4 +1,7 @@ -using Substrate.DotNet.Extensions; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis; +using Substrate.DotNet.Extensions; using Substrate.DotNet.Service.Node.Base; using Substrate.NetApi.Model.Meta; using Substrate.ServiceLayer.Storage; @@ -6,6 +9,9 @@ using System.CodeDom; using System.Collections.Generic; using System.Reflection; +using System.Linq; +using Newtonsoft.Json.Linq; +using System.Data; namespace Substrate.DotNet.Service.Node { @@ -23,96 +29,102 @@ public static RestServiceStorageModuleBuilder Init(string projectName, uint id, public override RestServiceStorageModuleBuilder Create() { - if (Module.Storage == null) { Success = false; return this; } - ImportsNamespace.Imports.Add(new CodeNamespaceImport("Substrate.ServiceLayer.Attributes")); - ImportsNamespace.Imports.Add(new CodeNamespaceImport("Substrate.ServiceLayer.Storage")); - ImportsNamespace.Imports.Add(new CodeNamespaceImport("System.Threading.Tasks")); - FileName = Module.Storage.Prefix + "Storage"; - ReferenzName = $"{ProjectName}.Generated.Storage.{FileName}"; NamespaceName = $"{ProjectName}.Generated.Storage"; - CodeNamespace typeNamespace = new(NamespaceName); - TargetUnit.Namespaces.Add(typeNamespace); + SyntaxList usingDirectives = new SyntaxList() + .Add(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("Substrate.ServiceLayer.Attributes"))) + .Add(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("Substrate.ServiceLayer.Storage"))) + .Add(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("System.Threading.Tasks"))); + + NamespaceDeclarationSyntax namespaceDeclaration = SyntaxFactory + .NamespaceDeclaration(SyntaxFactory.ParseName(NamespaceName)); + + TargetUnit = TargetUnit.AddUsings(usingDirectives.ToArray()); + TargetUnit = TargetUnit.AddMembers(CreateStorage(namespaceDeclaration)); - CreateStorage(typeNamespace); return this; } - private void CreateStorage(CodeNamespace typeNamespace) + private NamespaceDeclarationSyntax CreateStorage(NamespaceDeclarationSyntax namespaceDeclaration) { + // Setting ClassName ClassName = Module.Storage.Prefix + "Storage"; - var targetInterface = new CodeTypeDeclaration($"I{ClassName}") - { - IsInterface = true - - }; - targetInterface.Comments.AddRange(GetComments(new string[] { $"I{ClassName} interface definition." })); - targetInterface.BaseTypes.Add(new CodeTypeReference("IStorage")); - typeNamespace.Types.Add(targetInterface); - - - var targetClass = new CodeTypeDeclaration(ClassName) - { - IsClass = true, - TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed - }; - - targetClass.Comments.AddRange(GetComments(new string[] { $"{ClassName} class definition." })); - targetClass.BaseTypes.Add(new CodeTypeReference(targetInterface.Name)); + // Creating the interface declaration + InterfaceDeclarationSyntax targetInterface = SyntaxFactory + .InterfaceDeclaration($"I{ClassName}") + .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) + .AddBaseListTypes(SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName("IStorage"))) + .WithLeadingTrivia(GetCommentsRoslyn(new string[] { $"I{ClassName} interface definition" })); + + // Creating the class declaration + ClassDeclarationSyntax targetClass = SyntaxFactory.ClassDeclaration(ClassName) + .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.SealedKeyword)) + .AddBaseListTypes(SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName(targetInterface.Identifier.Text))) + .WithLeadingTrivia(GetCommentsRoslyn(new string[] { $"{ClassName} class definition" })); + + // Creating the constructor + ConstructorDeclarationSyntax constructor = SyntaxFactory.ConstructorDeclaration(ClassName) + .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) + .WithParameterList(SyntaxFactory.ParameterList() + .AddParameters( + SyntaxFactory.Parameter(SyntaxFactory.Identifier("storageDataProvider")) + .WithType(SyntaxFactory.ParseTypeName("IStorageDataProvider")), + SyntaxFactory.Parameter(SyntaxFactory.Identifier("storageChangeDelegates")) + .WithType(SyntaxFactory.ParseTypeName("List")) + )) + .WithLeadingTrivia(GetCommentsRoslyn(new string[] { $"{ClassName} constructor" })); + + // Creating the InitializeAsync method + MethodDeclarationSyntax initializeAsyncMethod = SyntaxFactory.MethodDeclaration(SyntaxFactory.ParseTypeName("Task"), "InitializeAsync") + .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.AsyncKeyword)) + .AddParameterListParameters( + SyntaxFactory.Parameter(SyntaxFactory.Identifier("dataProvider")) + .WithType(SyntaxFactory.ParseTypeName("Substrate.ServiceLayer.Storage.IStorageDataProvider")) + ) + .WithLeadingTrivia(GetCommentsRoslyn(new string[] { "Connects to all storages and initializes the change subscription handling" })); - typeNamespace.Types.Add(targetClass); - - CodeConstructor constructor = new() + if (Module.Storage.Entries != null) { - Attributes = MemberAttributes.Public | MemberAttributes.Final, - }; - - constructor.Parameters.Add(new CodeParameterDeclarationExpression(new CodeTypeReference("IStorageDataProvider"), "storageDataProvider")); - constructor.Parameters.Add(new CodeParameterDeclarationExpression(new CodeTypeReference("List"), "storageChangeDelegates")); - constructor.Comments.AddRange(GetComments(new string[] { $"{ClassName} constructor." })); + ParameterSyntax keyParamter = SyntaxFactory.Parameter(SyntaxFactory.Identifier("key")) + .WithType(SyntaxFactory.ParseTypeName("string")); - targetClass.Members.Add(constructor); + ParameterSyntax dataParamter = SyntaxFactory.Parameter(SyntaxFactory.Identifier("data")) + .WithType(SyntaxFactory.ParseTypeName("string")); - CodeMemberMethod initializeAsyncMethod = new() - { - Attributes = MemberAttributes.Public | MemberAttributes.Final, - Name = $"InitializeAsync", - ReturnType = new CodeTypeReference("async Task") + var constructorStatements = new List(); + var initializeStatements = new List(); - }; - var clientParamter = new CodeParameterDeclarationExpression(typeof(IStorageDataProvider), "dataProvider"); - initializeAsyncMethod.Parameters.Add(clientParamter); - targetClass.Members.Add(initializeAsyncMethod); - initializeAsyncMethod.Comments.AddRange(GetComments(new string[] { $"Connects to all storages and initializes the change subscription handling." })); + //var fields = new List(); + var props = new List(); - if (Module.Storage.Entries != null) - { - var keyParamter = new CodeParameterDeclarationExpression(typeof(string), "key"); - var dataParamter = new CodeParameterDeclarationExpression(typeof(string), "data"); + var interfaceMethods = new List(); + var methodOnAndGet = new List(); foreach (Entry entry in Module.Storage.Entries) { - CodeTypeReference baseReturnType; - CodeTypeReference returnType; - CodeExpression[] updateExpression, tryGetExpression; + TypeSyntax baseReturnType; + TypeSyntax returnType; + ArgumentListSyntax updateExpression, tryGetExpression; + if (entry.StorageType == Storage.Type.Plain) { NodeTypeResolved fullItem = GetFullItemPath(entry.TypeMap.Item1); - baseReturnType = new CodeTypeReference(fullItem.ToString()); - returnType = new CodeTypeReference($"TypedStorage<{fullItem.ToString()}>"); + baseReturnType = SyntaxFactory.ParseTypeName(fullItem.ToString()); + returnType = SyntaxFactory.ParseTypeName($"TypedStorage<{fullItem}>"); + + updateExpression = SyntaxFactory.ArgumentList().AddArguments( + SyntaxFactory.Argument(SyntaxFactory.IdentifierName(dataParamter.Identifier))); - updateExpression = new CodeExpression[] { - new CodeVariableReferenceExpression(dataParamter.Name)}; - tryGetExpression = Array.Empty(); + tryGetExpression = SyntaxFactory.ArgumentList(); } else if (entry.StorageType == Storage.Type.Map) { @@ -120,141 +132,199 @@ private void CreateStorage(CodeNamespace typeNamespace) Storage.Hasher[] hashers = typeMap.Hashers; NodeTypeResolved key = GetFullItemPath(typeMap.Key); NodeTypeResolved value = GetFullItemPath(typeMap.Value); - baseReturnType = new CodeTypeReference(value.ToString()); - returnType = new CodeTypeReference($"TypedMapStorage<{value.ToString()}>"); - - updateExpression = new CodeExpression[] { - new CodeVariableReferenceExpression(keyParamter.Name), - new CodeVariableReferenceExpression(dataParamter.Name)}; - tryGetExpression = new CodeExpression[] { - new CodeVariableReferenceExpression(keyParamter.Name), - new CodeParameterDeclarationExpression(baseReturnType, "result") { - Direction = FieldDirection.Out - } - }; + baseReturnType = SyntaxFactory.ParseTypeName(value.ToString()); + returnType = SyntaxFactory.ParseTypeName($"TypedMapStorage<{value}>"); + + updateExpression = SyntaxFactory.ArgumentList().AddArguments( + SyntaxFactory.Argument(SyntaxFactory.IdentifierName(keyParamter.Identifier)), + SyntaxFactory.Argument(SyntaxFactory.IdentifierName(dataParamter.Identifier))); + + tryGetExpression = SyntaxFactory.ArgumentList().AddArguments( + SyntaxFactory.Argument(SyntaxFactory.IdentifierName(keyParamter.Identifier)), + SyntaxFactory.Argument( + SyntaxFactory.DeclarationExpression( + baseReturnType, + SyntaxFactory.SingleVariableDesignation(SyntaxFactory.Identifier("result")))) + .WithRefOrOutKeyword(SyntaxFactory.Token(SyntaxKind.OutKeyword)) + ); + } else { throw new NotImplementedException(); } - // create typed storage field - CodeMemberField field = new() - { - Attributes = MemberAttributes.Private, - Name = $"{entry.Name.MakePrivateField()}TypedStorage", - Type = returnType - }; + string fieldName = $"{entry.Name.MakePrivateField()}TypedStorage"; - field.Comments.AddRange(GetComments(new string[] { $"{field.Name} typed storage field" })); - targetClass.Members.Add(field); + // create typed storage field + //FieldDeclarationSyntax field = SyntaxFactory.FieldDeclaration( + // SyntaxFactory.VariableDeclaration(returnType) + // .AddVariables(SyntaxFactory.VariableDeclarator(SyntaxFactory.Identifier(fieldName)))) + // .AddModifiers(SyntaxFactory.Token(SyntaxKind.PrivateKeyword)) + // .WithLeadingTrivia(GetCommentsRoslyn(new string[] { $"{entry.Name} typed storage field" })); + //fields.Add(field); // create typed storage property - CodeMemberProperty prop = new() - { - Attributes = MemberAttributes.Public | MemberAttributes.Final, - Name = field.Name.MakeMethod(), - HasGet = true, - Type = field.Type - }; - prop.GetStatements.Add(new CodeMethodReturnStatement( - new CodeVariableReferenceExpression(field.Name))); - prop.SetStatements.Add(new CodeAssignStatement( - new CodeVariableReferenceExpression(field.Name), - new CodePropertySetValueReferenceExpression())); - - - prop.Comments.AddRange(GetComments(new string[] { $"{field.Name} property" })); - targetClass.Members.Add(prop); + string propName = $"{entry.Name}TypedStorage"; + PropertyDeclarationSyntax prop = SyntaxFactory.PropertyDeclaration(returnType, SyntaxFactory.Identifier(propName)) + .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) + .AddAccessorListAccessors( + SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration) + .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)), + SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration) + .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))) + .WithLeadingTrivia(GetCommentsRoslyn(new string[] { $"{propName} property" })); + props.Add(prop); // constructor initialize storage properties - constructor.Statements.Add(new CodeAssignStatement( - new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), prop.Name), - new CodeObjectCreateExpression(field.Type, - new CodeExpression[] { - new CodePrimitiveExpression($"{Module.Storage.Prefix}.{entry.Name}"), - new CodeVariableReferenceExpression("storageDataProvider"), - new CodeVariableReferenceExpression("storageChangeDelegates") - }))); + constructorStatements.Add(SyntaxFactory.ExpressionStatement( + SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, + SyntaxFactory.IdentifierName(prop.Identifier.Text), + SyntaxFactory.ObjectCreationExpression(prop.Type) + .WithArgumentList(SyntaxFactory.ArgumentList() + .AddArguments( + SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal($"{Module.Storage.Prefix}.{entry.Name}"))), + SyntaxFactory.Argument(SyntaxFactory.IdentifierName("storageDataProvider")), + SyntaxFactory.Argument(SyntaxFactory.IdentifierName("storageChangeDelegates"))))))); // create initialize records foreach storage - CodeMethodInvokeExpression initializeAsyncInvoke = new( - new CodeVariableReferenceExpression($"await {prop.Name}"), - "InitializeAsync", new CodeExpression[] { - new CodePrimitiveExpression(Module.Storage.Prefix), - new CodePrimitiveExpression(entry.Name) - }); - initializeAsyncMethod.Statements.Add(initializeAsyncInvoke); - + initializeStatements.Add(SyntaxFactory.ExpressionStatement( + SyntaxFactory.InvocationExpression( + SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.IdentifierName($"await {prop.Identifier}"), + SyntaxFactory.IdentifierName("InitializeAsync"))) + .WithArgumentList(SyntaxFactory.ArgumentList() + .AddArguments( + SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(Module.Storage.Prefix))), + SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(entry.Name))))))); + // create on update - CodeMemberMethod onUpdateMethod = new() - { - Attributes = MemberAttributes.Public | MemberAttributes.Final, - Name = $"OnUpdate{entry.Name}", - }; + MethodDeclarationSyntax onUpdateMethod = SyntaxFactory.MethodDeclaration( + SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.VoidKeyword)), + SyntaxFactory.Identifier($"OnUpdate{entry.Name}")) + .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) + .AddAttributeLists( + SyntaxFactory.AttributeList( + SyntaxFactory.SeparatedList( + new AttributeSyntax[] { + SyntaxFactory.Attribute( + SyntaxFactory.IdentifierName("StorageChange"), + SyntaxFactory.AttributeArgumentList( + SyntaxFactory.SeparatedList( + new AttributeArgumentSyntax[]{ + SyntaxFactory.AttributeArgument( + SyntaxFactory.LiteralExpression( + SyntaxKind.StringLiteralExpression, + SyntaxFactory.Literal(Module.Storage.Prefix))), + SyntaxFactory.AttributeArgument( + SyntaxFactory.LiteralExpression( + SyntaxKind.StringLiteralExpression, + SyntaxFactory.Literal(entry.Name)))})))}))) + .WithBody( + SyntaxFactory.Block( + SyntaxFactory.ExpressionStatement( + SyntaxFactory.InvocationExpression( + SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.IdentifierName(prop.Identifier.Text), + SyntaxFactory.IdentifierName("Update") + ), + SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList( + updateExpression.Arguments.Select(expr => expr))))))); + onUpdateMethod = onUpdateMethod.WithLeadingTrivia(GetCommentsRoslyn(new string[] { $"Implements any storage change for {Module.Storage.Prefix}.{entry.Name}" })); - onUpdateMethod.CustomAttributes.Add( - new CodeAttributeDeclaration("StorageChange", - new CodeAttributeArgument[] { - new CodeAttributeArgument(new CodePrimitiveExpression(Module.Storage.Prefix)), - new CodeAttributeArgument(new CodePrimitiveExpression(entry.Name)) - })); - - CodeMethodInvokeExpression updateInvoke = new( - new CodeVariableReferenceExpression(prop.Name), - "Update", updateExpression); - onUpdateMethod.Statements.Add(updateInvoke); - onUpdateMethod.Comments.AddRange(GetComments(new string[] { $"Implements any storage change for {Module.Storage.Prefix}.{entry.Name}" })); - - targetClass.Members.Add(onUpdateMethod); // create get and gets - CodeMemberMethod getStorageMethod = new() - { - Attributes = MemberAttributes.Public | MemberAttributes.Final, - Name = $"Get{entry.Name}", - ReturnType = baseReturnType - }; - getStorageMethod.Comments.AddRange(GetComments(entry.Docs, null, entry.Name)); + MethodDeclarationSyntax getInterfaceMethod = SyntaxFactory + .MethodDeclaration(baseReturnType, $"Get{entry.Name}") + .WithLeadingTrivia(GetCommentsRoslyn(entry.Docs, null, entry.Name)) + .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)); - targetInterface.Members.Add(getStorageMethod); + MethodDeclarationSyntax getStorageMethod = SyntaxFactory + .MethodDeclaration(baseReturnType, $"Get{entry.Name}") + .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) + .WithLeadingTrivia(GetCommentsRoslyn(entry.Docs, null, entry.Name)); - if (tryGetExpression.Length == 0) + if (tryGetExpression.Arguments.Count == 0) { - onUpdateMethod.Parameters.Add(dataParamter); - - getStorageMethod.Statements.Add(new CodeMethodReturnStatement( - new CodeMethodInvokeExpression( - new CodeVariableReferenceExpression(prop.Name), - "Get", Array.Empty()))); + onUpdateMethod = onUpdateMethod + .AddParameterListParameters(dataParamter); + + getStorageMethod = getStorageMethod.WithBody( + SyntaxFactory.Block( + SyntaxFactory.ReturnStatement( + SyntaxFactory.InvocationExpression( + SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.IdentifierName(prop.Identifier.Text), + SyntaxFactory.IdentifierName("Get")))))); } else { - onUpdateMethod.Parameters.Add(keyParamter); - onUpdateMethod.Parameters.Add(dataParamter); - - getStorageMethod.Parameters.Add(keyParamter); - - getStorageMethod.Statements.Add(new CodeConditionStatement( - new CodeBinaryOperatorExpression( - new CodeVariableReferenceExpression("key"), - CodeBinaryOperatorType.ValueEquality, - new CodePrimitiveExpression(null)), - new CodeStatement[] { new CodeMethodReturnStatement(new CodePrimitiveExpression(null)) })); - - getStorageMethod.Statements.Add(new CodeConditionStatement( - new CodeMethodInvokeExpression( - new CodeVariableReferenceExpression(prop.Name), - "Dictionary.TryGetValue", tryGetExpression), - new CodeStatement[] { new CodeMethodReturnStatement(new CodeVariableReferenceExpression("result")) }, - new CodeStatement[] { new CodeMethodReturnStatement(new CodePrimitiveExpression(null)) })); - } + onUpdateMethod = onUpdateMethod + .AddParameterListParameters(keyParamter, dataParamter); + + getInterfaceMethod = getInterfaceMethod.AddParameterListParameters( + SyntaxFactory.Parameter(keyParamter.Identifier) + .WithType(keyParamter.Type)); + + getStorageMethod = getStorageMethod.AddParameterListParameters( + SyntaxFactory.Parameter(keyParamter.Identifier) + .WithType(keyParamter.Type)); + + getStorageMethod = getStorageMethod.WithBody( + SyntaxFactory.Block( + SyntaxFactory.IfStatement( + SyntaxFactory.BinaryExpression( + SyntaxKind.EqualsExpression, + SyntaxFactory.IdentifierName("key"), + SyntaxFactory.LiteralExpression(SyntaxKind.NullLiteralExpression)), + SyntaxFactory.Block( + SyntaxFactory.ReturnStatement( + SyntaxFactory.LiteralExpression(SyntaxKind.NullLiteralExpression)))), + SyntaxFactory.IfStatement( + SyntaxFactory.InvocationExpression( + SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.IdentifierName(prop.Identifier.Text), + SyntaxFactory.IdentifierName("Dictionary.TryGetValue")), + tryGetExpression), + SyntaxFactory.Block( + SyntaxFactory.ReturnStatement( + SyntaxFactory.IdentifierName("result"))), + SyntaxFactory.ElseClause( + SyntaxFactory.ReturnStatement( + SyntaxFactory.LiteralExpression(SyntaxKind.NullLiteralExpression)))))); - targetClass.Members.Add(getStorageMethod); + } + interfaceMethods.Add(getInterfaceMethod); + methodOnAndGet.Add(onUpdateMethod); + methodOnAndGet.Add(getStorageMethod); } + + //targetClass = targetClass.AddMembers(fields.ToArray()); + targetInterface = targetInterface.AddMembers(interfaceMethods.ToArray()); + + constructor = constructor.WithBody(SyntaxFactory.Block(constructorStatements)); + targetClass = targetClass.AddMembers(constructor); + + targetClass = targetClass.AddMembers(props.ToArray()); + + initializeAsyncMethod = initializeAsyncMethod.WithBody(SyntaxFactory.Block(initializeStatements)); + targetClass = targetClass.AddMembers(initializeAsyncMethod); + + targetClass = targetClass.AddMembers(methodOnAndGet.ToArray()); + + namespaceDeclaration = namespaceDeclaration.AddMembers(targetInterface); + + namespaceDeclaration = namespaceDeclaration.AddMembers(targetClass); + } + + return namespaceDeclaration; } + } -} +} \ No newline at end of file diff --git a/Tools/Substrate.DotNet/Service/Node/RunetimeBuilder.cs b/Tools/Substrate.DotNet/Service/Node/RunetimeBuilder.cs deleted file mode 100644 index 6907dd4..0000000 --- a/Tools/Substrate.DotNet/Service/Node/RunetimeBuilder.cs +++ /dev/null @@ -1,64 +0,0 @@ -using Substrate.DotNet.Service.Node.Base; -using Substrate.NetApi.Model.Meta; -using System.CodeDom; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; - -namespace Substrate.DotNet.Service.Node -{ - public class RunetimeBuilder : TypeBuilderBase - { - private RunetimeBuilder(string projectName, uint id, NodeTypeVariant typeDef, NodeTypeResolver typeDict) - : base(projectName, id, typeDef, typeDict) - { - } - - public static RunetimeBuilder Init(string projectName, uint id, NodeTypeVariant typeDef, NodeTypeResolver typeDict) - { - return new RunetimeBuilder(projectName, id, typeDef, typeDict); - } - - public override TypeBuilderBase Create() - { - var typeDef = TypeDef as NodeTypeVariant; - - string runtimeType = $"{typeDef.Path.Last()}"; - string enumName = runtimeType; - - ClassName = $"Enum{enumName}"; - ReferenzName = $"{NamespaceName}.{ClassName}"; - CodeNamespace typeNamespace = new(NamespaceName); - TargetUnit.Namespaces.Add(typeNamespace); - - CodeTypeDeclaration TargetType = new(enumName) - { - IsEnum = true - }; - - if (typeDef.Variants != null) - { - foreach (string enumFieldName in typeDef.Variants.Select(p => p.Name)) - { - TargetType.Members.Add(new CodeMemberField(ClassName, enumFieldName)); - } - } - typeNamespace.Types.Add(TargetType); - - var targetClass = new CodeTypeDeclaration(ClassName) - { - IsClass = true, - TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed - }; - targetClass.BaseTypes.Add(new CodeTypeReference($"BaseEnum<{enumName}>")); - - // add comment to class if exists - targetClass.Comments.AddRange(GetComments(typeDef.Docs, typeDef)); - AddTargetClassCustomAttributes(targetClass, typeDef); - - typeNamespace.Types.Add(targetClass); - - return this; - } - } -} diff --git a/Tools/Substrate.DotNet/Service/Node/StructBuilder.cs b/Tools/Substrate.DotNet/Service/Node/StructBuilder.cs index 1ec9cb9..6564b54 100644 --- a/Tools/Substrate.DotNet/Service/Node/StructBuilder.cs +++ b/Tools/Substrate.DotNet/Service/Node/StructBuilder.cs @@ -1,10 +1,11 @@ -using Substrate.DotNet.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Substrate.DotNet.Extensions; using Substrate.DotNet.Service.Node.Base; using Substrate.NetApi.Model.Meta; -using System.CodeDom; using System.Collections.Generic; using System.Linq; -using System.Reflection; namespace Substrate.DotNet.Service.Node { @@ -15,54 +16,64 @@ private StructBuilder(string projectName, uint id, NodeTypeComposite typeDef, No { } - private static CodeMemberField GetPropertyField(string name, string baseType) + private static FieldDeclarationSyntax GetPropertyFieldRoslyn(string name, string baseType) { - CodeMemberField field = new() - { - Attributes = MemberAttributes.Private, - Name = name.MakePrivateField(), - Type = new CodeTypeReference($"{baseType}") - }; - return field; + FieldDeclarationSyntax fieldDeclaration = SyntaxFactory.FieldDeclaration( + SyntaxFactory.VariableDeclaration( + SyntaxFactory.ParseTypeName(baseType), + SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.VariableDeclarator(name.MakePrivateField())))) + .AddModifiers(SyntaxFactory.Token(SyntaxKind.PrivateKeyword)); + + return fieldDeclaration; } - private static CodeMemberProperty GetProperty(string name, CodeMemberField propertyField) + private static PropertyDeclarationSyntax GetPropertyWithFieldRoslyn(string name, FieldDeclarationSyntax propertyField) { - CodeMemberProperty prop = new() - { - Attributes = MemberAttributes.Public | MemberAttributes.Final, - Name = name.MakeMethod(), - HasGet = true, - HasSet = true, - Type = propertyField.Type - }; - prop.GetStatements.Add(new CodeMethodReturnStatement( - new CodeFieldReferenceExpression( - new CodeThisReferenceExpression(), propertyField.Name))); - prop.SetStatements.Add(new CodeAssignStatement( - new CodeFieldReferenceExpression( - new CodeThisReferenceExpression(), propertyField.Name), - new CodePropertySetValueReferenceExpression())); + string propertyName = name.MakeMethod(); + TypeSyntax propertyType = propertyField.Declaration.Type; + + PropertyDeclarationSyntax prop = SyntaxFactory.PropertyDeclaration(propertyType, propertyName) + .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) + .AddAccessorListAccessors( + SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration) + .WithBody(SyntaxFactory.Block(SyntaxFactory.ReturnStatement(SyntaxFactory.IdentifierName(propertyField.Declaration.Variables[0].Identifier)))), + SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration) + .WithBody(SyntaxFactory.Block( + SyntaxFactory.ExpressionStatement( + SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, + SyntaxFactory.IdentifierName(propertyField.Declaration.Variables[0].Identifier), + SyntaxFactory.IdentifierName("value")))))); + return prop; } - private CodeMemberMethod GetDecode(NodeTypeField[] typeFields) + private static PropertyDeclarationSyntax GetPropertyRoslyn(string name, TypeSyntax type) { - CodeMemberMethod decodeMethod = SimpleMethod("Decode"); - CodeParameterDeclarationExpression param1 = new() - { - Type = new CodeTypeReference("System.Byte[]"), - Name = "byteArray" - }; - decodeMethod.Parameters.Add(param1); - CodeParameterDeclarationExpression param2 = new() - { - Type = new CodeTypeReference("System.Int32"), - Name = "p", - Direction = FieldDirection.Ref - }; - decodeMethod.Parameters.Add(param2); - decodeMethod.Statements.Add(new CodeSnippetExpression("var start = p")); + string propertyName = name.MakeMethod(); + + PropertyDeclarationSyntax prop = SyntaxFactory.PropertyDeclaration(type, propertyName) + .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) + .AddAccessorListAccessors( + SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration) + .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)), + SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration) + .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))); + + return prop; + } + + private MethodDeclarationSyntax GetDecodeRoslyn(NodeTypeField[] typeFields) + { + MethodDeclarationSyntax decodeMethod = SyntaxFactory.MethodDeclaration(SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.VoidKeyword)), "Decode") + .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) + .AddModifiers(SyntaxFactory.Token(SyntaxKind.OverrideKeyword)) + .AddParameterListParameters( + SyntaxFactory.Parameter(SyntaxFactory.Identifier("byteArray")).WithType(SyntaxFactory.ParseTypeName("byte[]")), + SyntaxFactory.Parameter(SyntaxFactory.Identifier("p")).WithType(SyntaxFactory.ParseTypeName("int")).WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.RefKeyword)))) + .WithBody(SyntaxFactory.Block()); + + decodeMethod = decodeMethod.AddBodyStatements(SyntaxFactory.ParseStatement("var start = p;")); if (typeFields != null) { @@ -70,30 +81,31 @@ private CodeMemberMethod GetDecode(NodeTypeField[] typeFields) { NodeTypeField typeField = typeFields[i]; - string fieldName = StructBuilder.GetFieldName(typeField, "value", typeFields.Length, i); + string fieldName = GetFieldName(typeField, "value", typeFields.Length, i); NodeTypeResolved fullItem = GetFullItemPath(typeField.TypeId); - decodeMethod.Statements.Add(new CodeSnippetExpression($"{fieldName.MakeMethod()} = new {fullItem.ToString()}()")); - decodeMethod.Statements.Add(new CodeSnippetExpression($"{fieldName.MakeMethod()}.Decode(byteArray, ref p)")); + decodeMethod = decodeMethod.AddBodyStatements(SyntaxFactory.ParseStatement($"{fieldName.MakeMethod()} = new {fullItem}();")); + decodeMethod = decodeMethod.AddBodyStatements(SyntaxFactory.ParseStatement($"{fieldName.MakeMethod()}.Decode(byteArray, ref p);")); } } - decodeMethod.Statements.Add(new CodeSnippetExpression("var bytesLength = p - start")); - decodeMethod.Statements.Add(new CodeSnippetExpression("TypeSize = bytesLength")); - decodeMethod.Statements.Add(new CodeSnippetExpression("Bytes = new byte[bytesLength]")); - decodeMethod.Statements.Add(new CodeSnippetExpression("System.Array.Copy(byteArray, start, Bytes, 0, bytesLength)")); + + decodeMethod = decodeMethod.AddBodyStatements( + SyntaxFactory.ParseStatement("var bytesLength = p - start;"), + SyntaxFactory.ParseStatement("TypeSize = bytesLength;"), + SyntaxFactory.ParseStatement("Bytes = new byte[bytesLength];"), + SyntaxFactory.ParseStatement("System.Array.Copy(byteArray, start, Bytes, 0, bytesLength);") + ); return decodeMethod; } - private static CodeMemberMethod GetEncode(NodeTypeField[] typeFields) + private MethodDeclarationSyntax GetEncodeRoslyn(NodeTypeField[] typeFields) { - CodeMemberMethod encodeMethod = new() - { - Attributes = MemberAttributes.Public | MemberAttributes.Override, - Name = "Encode", - ReturnType = new CodeTypeReference("System.Byte[]") - }; - encodeMethod.Statements.Add(new CodeSnippetExpression("var result = new List()")); + MethodDeclarationSyntax encodeMethod = SyntaxFactory.MethodDeclaration(SyntaxFactory.ParseTypeName("System.Byte[]"), "Encode") + .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.OverrideKeyword)) + .WithBody(SyntaxFactory.Block()); + + encodeMethod = encodeMethod.AddBodyStatements(SyntaxFactory.ParseStatement("var result = new List();")); if (typeFields != null) { @@ -102,11 +114,12 @@ private static CodeMemberMethod GetEncode(NodeTypeField[] typeFields) NodeTypeField typeField = typeFields[i]; string fieldName = StructBuilder.GetFieldName(typeField, "value", typeFields.Length, i); - encodeMethod.Statements.Add(new CodeSnippetExpression($"result.AddRange({fieldName.MakeMethod()}.Encode())")); + encodeMethod = encodeMethod.AddBodyStatements(SyntaxFactory.ParseStatement($"result.AddRange({fieldName.MakeMethod()}.Encode());")); } } - encodeMethod.Statements.Add(new CodeSnippetExpression("return result.ToArray()")); + encodeMethod = encodeMethod.AddBodyStatements(SyntaxFactory.ParseStatement("return result.ToArray();")); + return encodeMethod; } @@ -121,52 +134,55 @@ public override TypeBuilderBase Create() ClassName = $"{typeDef.Path.Last()}"; - ReferenzName = $"{NamespaceName}.{typeDef.Path.Last()}"; + ReferenzName = $"{NamespaceName}.{ClassName}"; - CodeNamespace typeNamespace = new(NamespaceName); - TargetUnit.Namespaces.Add(typeNamespace); - - var targetClass = new CodeTypeDeclaration(ClassName) - { - IsClass = true, - TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed - }; - targetClass.BaseTypes.Add(new CodeTypeReference("BaseType")); + ClassDeclarationSyntax targetClass = SyntaxFactory.ClassDeclaration(ClassName) + .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.SealedKeyword)) + .AddBaseListTypes(SyntaxFactory.SimpleBaseType(SyntaxFactory.IdentifierName("BaseType"))); + targetClass = AddTargetClassCustomAttributesRoslyn(targetClass, typeDef); // add comment to class if exists - targetClass.Comments.AddRange(GetComments(typeDef.Docs, typeDef)); - AddTargetClassCustomAttributes(targetClass, typeDef); - - typeNamespace.Types.Add(targetClass); - - CodeMemberMethod nameMethod = SimpleMethod("TypeName", "System.String", ClassName); - targetClass.Members.Add(nameMethod); + targetClass = targetClass.WithLeadingTrivia(GetCommentsRoslyn(typeDef.Docs, typeDef)); if (typeDef.TypeFields != null) { for (int i = 0; i < typeDef.TypeFields.Length; i++) { - NodeTypeField typeField = typeDef.TypeFields[i]; - string fieldName = StructBuilder.GetFieldName(typeField, "value", typeDef.TypeFields.Length, i); + string fieldName = GetFieldName(typeField, "value", typeDef.TypeFields.Length, i); NodeTypeResolved fullItem = GetFullItemPath(typeField.TypeId); - CodeMemberField field = StructBuilder.GetPropertyField(fieldName, fullItem.ToString()); - + //FieldDeclarationSyntax field = GetPropertyFieldRoslyn(fieldName, fullItem.ToString()); // add comment to field if exists - field.Comments.AddRange(GetComments(typeField.Docs, null, fieldName)); + //field = field.WithLeadingTrivia(GetCommentsRoslyn(typeField.Docs, null, fieldName)); + //targetClass = targetClass.AddMembers(field, GetPropertyWithFieldRoslyn(fieldName, field)); - targetClass.Members.Add(field); - targetClass.Members.Add(StructBuilder.GetProperty(fieldName, field)); + PropertyDeclarationSyntax propertyDeclaration = GetPropertyRoslyn(fieldName, SyntaxFactory.ParseTypeName(fullItem.ToString())); + propertyDeclaration = propertyDeclaration.WithLeadingTrivia(GetCommentsRoslyn(typeField.Docs, null, fieldName)); + + targetClass = targetClass.AddMembers(propertyDeclaration); } } - CodeMemberMethod encodeMethod = StructBuilder.GetEncode(typeDef.TypeFields); - targetClass.Members.Add(encodeMethod); + MethodDeclarationSyntax nameMethod = SimpleMethodRoslyn("TypeName", "System.String", ClassName); + targetClass = targetClass.AddMembers(nameMethod); + + MethodDeclarationSyntax encodeMethod = GetEncodeRoslyn(typeDef.TypeFields); + targetClass = targetClass.AddMembers(encodeMethod); + + MethodDeclarationSyntax decodeMethod = GetDecodeRoslyn(typeDef.TypeFields); + targetClass = targetClass.AddMembers(decodeMethod); + + NamespaceDeclarationSyntax namespaceDeclaration = SyntaxFactory + .NamespaceDeclaration(SyntaxFactory.IdentifierName(NamespaceName)) + .AddMembers(targetClass); + + CompilationUnitSyntax compilationUnit = SyntaxFactory.CompilationUnit() + .AddUsings(SyntaxFactory.UsingDirective(SyntaxFactory.IdentifierName("System"))) + .AddMembers(namespaceDeclaration); - CodeMemberMethod decodeMethod = GetDecode(typeDef.TypeFields); - targetClass.Members.Add(decodeMethod); + TargetUnit = TargetUnit.AddMembers(compilationUnit.Members.ToArray()); return this; } @@ -197,4 +213,4 @@ private static string GetFieldName(NodeTypeField typeField, string alterName, in } } } -} +} \ No newline at end of file diff --git a/Tools/Substrate.DotNet/Substrate.DotNet.csproj b/Tools/Substrate.DotNet/Substrate.DotNet.csproj index 04fa088..dc6c5b9 100644 --- a/Tools/Substrate.DotNet/Substrate.DotNet.csproj +++ b/Tools/Substrate.DotNet/Substrate.DotNet.csproj @@ -39,6 +39,7 @@ +