From 2ac07820c3004d0cc276759ec2e1f1a786358689 Mon Sep 17 00:00:00 2001 From: "cedric.decoster" Date: Mon, 29 May 2023 09:42:37 +0200 Subject: [PATCH 01/14] code cleanup to prepare refactoring --- .../Substrate.DotNet/Service/Node/ArrayBuilder.cs | 9 +++------ .../Service/Node/Base/BuilderBase.cs | 8 +++----- .../Service/Node/Base/TypeBuilderBase.cs | 6 +----- .../Service/Node/ClientBuilder.cs | 4 +--- .../Substrate.DotNet/Service/Node/EnumBuilder.cs | 5 +---- .../Service/Node/EventModuleBuilder.cs | 3 +-- .../Substrate.DotNet/Service/Node/GetMetadata.cs | 4 ++-- .../Service/Node/NodeTypeResolver.cs | 15 +++++++++++---- .../Node/RestServiceControllerModuleBuilder.cs | 9 +-------- .../Node/RestServiceStorageModuleBuilder.cs | 9 +-------- .../Service/Node/RunetimeBuilder.cs | 3 +-- .../Service/Node/StructBuilder.cs | 4 +--- 12 files changed, 27 insertions(+), 52 deletions(-) diff --git a/Tools/Substrate.DotNet/Service/Node/ArrayBuilder.cs b/Tools/Substrate.DotNet/Service/Node/ArrayBuilder.cs index b6153d9..4bee00e 100644 --- a/Tools/Substrate.DotNet/Service/Node/ArrayBuilder.cs +++ b/Tools/Substrate.DotNet/Service/Node/ArrayBuilder.cs @@ -2,7 +2,6 @@ using Substrate.NetApi.Model.Meta; using System; using System.CodeDom; -using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -11,6 +10,7 @@ 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) { @@ -108,7 +108,7 @@ public override TypeBuilderBase Create() 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"); @@ -134,14 +134,12 @@ public override TypeBuilderBase Create() sizeProperty.GetStatements.Add(new CodeMethodReturnStatement(new CodePrimitiveExpression((int)typeDef.Length))); targetClass.Members.Add(sizeProperty); - CodeMemberMethod encodeMethod = ArrayBuilder.GetEncode(); targetClass.Members.Add(encodeMethod); CodeMemberMethod decodeMethod = ArrayBuilder.GetDecode(fullItem.ToString()); targetClass.Members.Add(decodeMethod); - CodeMemberField valueField = new() { Attributes = MemberAttributes.Private, @@ -165,7 +163,6 @@ public override TypeBuilderBase Create() new CodeThisReferenceExpression(), valueField.Name), new CodePropertySetValueReferenceExpression())); - CodeMemberMethod createMethod = new() { Attributes = MemberAttributes.Public | MemberAttributes.Final, @@ -184,4 +181,4 @@ public override TypeBuilderBase Create() 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..649a6af 100644 --- a/Tools/Substrate.DotNet/Service/Node/Base/BuilderBase.cs +++ b/Tools/Substrate.DotNet/Service/Node/Base/BuilderBase.cs @@ -7,14 +7,13 @@ namespace Substrate.DotNet.Service.Node.Base { - public abstract class BuilderBase { public static readonly List Files = new(); public uint Id { get; } - - NodeTypeResolver Resolver { get; } + + private NodeTypeResolver Resolver { get; } public bool Success { get; set; } @@ -153,7 +152,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 @@ -176,7 +175,6 @@ protected void AddTargetClassCustomAttributes(CodeTypeDeclaration targetClass, N targetClass.CustomAttributes.Add(new CodeAttributeDeclaration(new CodeTypeReference("SubstrateNodeType"), new CodeAttributeArgument( new CodeSnippetExpression($"TypeDefEnum.{typeDef.TypeDef}") ))); - } } } \ 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..d4efbeb 100644 --- a/Tools/Substrate.DotNet/Service/Node/ClientBuilder.cs +++ b/Tools/Substrate.DotNet/Service/Node/ClientBuilder.cs @@ -110,7 +110,5 @@ public override ClientBuilder Create() 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..bb54a98 100644 --- a/Tools/Substrate.DotNet/Service/Node/EnumBuilder.cs +++ b/Tools/Substrate.DotNet/Service/Node/EnumBuilder.cs @@ -3,8 +3,6 @@ using Substrate.NetApi.Model.Types; using System; using System.CodeDom; -using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Reflection; @@ -130,7 +128,6 @@ public override TypeBuilderBase Create() private CodeTypeMemberCollection GetEnumEra() { - ImportsNamespace.Imports.Add(new CodeNamespaceImport("Substrate.NetApi.Model.Types")); ImportsNamespace.Imports.Add(new CodeNamespaceImport("Substrate.NetApi.Model.Types.Primitive")); @@ -280,4 +277,4 @@ private CodeTypeMemberCollection GetEnumData() return result; } } -} +} \ No newline at end of file diff --git a/Tools/Substrate.DotNet/Service/Node/EventModuleBuilder.cs b/Tools/Substrate.DotNet/Service/Node/EventModuleBuilder.cs index 8a55774..2d41ec8 100644 --- a/Tools/Substrate.DotNet/Service/Node/EventModuleBuilder.cs +++ b/Tools/Substrate.DotNet/Service/Node/EventModuleBuilder.cs @@ -84,7 +84,6 @@ public override EventModuleBuilder Create() new CodePrimitiveExpression( variant.Index) }), new CodeTypeOfExpression(ReferenzName + "." + eventClass.Name) - }, "_client.EventKeyDict")); typeNamespace.Types.Add(eventClass); @@ -104,4 +103,4 @@ private static CodeStatement AddPropertyValues(CodeExpression[] exprs, string va new CodeTypeReference(variableReference)), "Add"), exprs)); } } -} +} \ No newline at end of file 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/NodeTypeResolver.cs b/Tools/Substrate.DotNet/Service/Node/NodeTypeResolver.cs index 2f81005..2ab9454 100644 --- a/Tools/Substrate.DotNet/Service/Node/NodeTypeResolver.cs +++ b/Tools/Substrate.DotNet/Service/Node/NodeTypeResolver.cs @@ -1,11 +1,11 @@ #nullable enable + 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 @@ -65,15 +65,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 +89,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) @@ -238,8 +244,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 +284,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 +331,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..1fb34b4 100644 --- a/Tools/Substrate.DotNet/Service/Node/RestServiceControllerModuleBuilder.cs +++ b/Tools/Substrate.DotNet/Service/Node/RestServiceControllerModuleBuilder.cs @@ -25,7 +25,6 @@ public static RestServiceControllerModuleBuilder Init(string projectName, string public override RestServiceControllerModuleBuilder Create() { - if (Module.Storage == null) { Success = false; @@ -56,12 +55,10 @@ private void CreateController(CodeNamespace typeNamespace) { IsClass = true, TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed - }; targetClass.BaseTypes.Add(new CodeTypeReference("ControllerBase")); targetClass.Comments.AddRange(GetComments(new string[] { $"{ClassName} controller to access storages." })); - typeNamespace.Types.Add(targetClass); targetClass.CustomAttributes.Add( new CodeAttributeDeclaration("ApiController")); @@ -95,7 +92,6 @@ private void CreateController(CodeNamespace typeNamespace) new CodeVariableReferenceExpression(field.Name), new CodeVariableReferenceExpression(fieldName.MakePublicField()))); - if (Module.Storage.Entries != null) { foreach (Entry entry in Module.Storage.Entries) @@ -175,7 +171,6 @@ private void CreateController(CodeNamespace typeNamespace) throw new NotImplementedException(); } - if (parameterDeclaration != null) { getStorageMethod.Parameters.Add(parameterDeclaration); @@ -193,10 +188,8 @@ private void CreateController(CodeNamespace typeNamespace) ))); targetClass.Members.Add(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..330b8e2 100644 --- a/Tools/Substrate.DotNet/Service/Node/RestServiceStorageModuleBuilder.cs +++ b/Tools/Substrate.DotNet/Service/Node/RestServiceStorageModuleBuilder.cs @@ -23,7 +23,6 @@ public static RestServiceStorageModuleBuilder Init(string projectName, uint id, public override RestServiceStorageModuleBuilder Create() { - if (Module.Storage == null) { Success = false; @@ -53,13 +52,11 @@ private void CreateStorage(CodeNamespace typeNamespace) 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, @@ -87,7 +84,6 @@ private void CreateStorage(CodeNamespace typeNamespace) Attributes = MemberAttributes.Public | MemberAttributes.Final, Name = $"InitializeAsync", ReturnType = new CodeTypeReference("async Task") - }; var clientParamter = new CodeParameterDeclarationExpression(typeof(IStorageDataProvider), "dataProvider"); initializeAsyncMethod.Parameters.Add(clientParamter); @@ -163,7 +159,6 @@ private void CreateStorage(CodeNamespace typeNamespace) new CodeVariableReferenceExpression(field.Name), new CodePropertySetValueReferenceExpression())); - prop.Comments.AddRange(GetComments(new string[] { $"{field.Name} property" })); targetClass.Members.Add(prop); @@ -251,10 +246,8 @@ private void CreateStorage(CodeNamespace typeNamespace) } targetClass.Members.Add(getStorageMethod); - - } } } } -} +} \ No newline at end of file diff --git a/Tools/Substrate.DotNet/Service/Node/RunetimeBuilder.cs b/Tools/Substrate.DotNet/Service/Node/RunetimeBuilder.cs index 6907dd4..ab98dad 100644 --- a/Tools/Substrate.DotNet/Service/Node/RunetimeBuilder.cs +++ b/Tools/Substrate.DotNet/Service/Node/RunetimeBuilder.cs @@ -1,7 +1,6 @@ using Substrate.DotNet.Service.Node.Base; using Substrate.NetApi.Model.Meta; using System.CodeDom; -using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -61,4 +60,4 @@ public override TypeBuilderBase Create() return this; } } -} +} \ No newline at end of file diff --git a/Tools/Substrate.DotNet/Service/Node/StructBuilder.cs b/Tools/Substrate.DotNet/Service/Node/StructBuilder.cs index 1ec9cb9..df56751 100644 --- a/Tools/Substrate.DotNet/Service/Node/StructBuilder.cs +++ b/Tools/Substrate.DotNet/Service/Node/StructBuilder.cs @@ -2,7 +2,6 @@ using Substrate.DotNet.Service.Node.Base; using Substrate.NetApi.Model.Meta; using System.CodeDom; -using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -146,7 +145,6 @@ public override TypeBuilderBase Create() { for (int i = 0; i < typeDef.TypeFields.Length; i++) { - NodeTypeField typeField = typeDef.TypeFields[i]; string fieldName = StructBuilder.GetFieldName(typeField, "value", typeDef.TypeFields.Length, i); @@ -197,4 +195,4 @@ private static string GetFieldName(NodeTypeField typeField, string alterName, in } } } -} +} \ No newline at end of file From c93dadd0910072095106e9e1e732126212218d44 Mon Sep 17 00:00:00 2001 From: "cedric.decoster" Date: Mon, 29 May 2023 14:54:00 +0200 Subject: [PATCH 02/14] migrated StructBuilder to Roslyn migrated ArrayBuilder to Roslyn --- .../Generators/Base/SolutionGeneratorBase.cs | 2 +- .../Service/Node/ArrayBuilderRoslyn.cs | 168 ++++++++++++++ .../Service/Node/Base/BuilderBaseRoslyn.cs | 181 +++++++++++++++ .../Node/Base/TypeBuilderBaseRoslyn.cs | 16 ++ .../Service/Node/StructBuilderRoslyn.cs | 216 ++++++++++++++++++ .../Substrate.DotNet/Substrate.DotNet.csproj | 1 + 6 files changed, 583 insertions(+), 1 deletion(-) create mode 100644 Tools/Substrate.DotNet/Service/Node/ArrayBuilderRoslyn.cs create mode 100644 Tools/Substrate.DotNet/Service/Node/Base/BuilderBaseRoslyn.cs create mode 100644 Tools/Substrate.DotNet/Service/Node/Base/TypeBuilderBaseRoslyn.cs create mode 100644 Tools/Substrate.DotNet/Service/Node/StructBuilderRoslyn.cs diff --git a/Tools/Substrate.DotNet/Service/Generators/Base/SolutionGeneratorBase.cs b/Tools/Substrate.DotNet/Service/Generators/Base/SolutionGeneratorBase.cs index c70804d..49a2660 100644 --- a/Tools/Substrate.DotNet/Service/Generators/Base/SolutionGeneratorBase.cs +++ b/Tools/Substrate.DotNet/Service/Generators/Base/SolutionGeneratorBase.cs @@ -79,7 +79,7 @@ protected NodeTypeResolver GenerateTypes(Dictionary nodeTypes, s case TypeDefEnum.Array: { var type = nodeType as NodeTypeArray; - ArrayBuilder.Create(ProjectName, type.Id, type, resolver) + ArrayBuilderRoslyn.Create(ProjectName, type.Id, type, resolver) .Create() .Build(write: write, out bool success, basePath); diff --git a/Tools/Substrate.DotNet/Service/Node/ArrayBuilderRoslyn.cs b/Tools/Substrate.DotNet/Service/Node/ArrayBuilderRoslyn.cs new file mode 100644 index 0000000..2c03b1e --- /dev/null +++ b/Tools/Substrate.DotNet/Service/Node/ArrayBuilderRoslyn.cs @@ -0,0 +1,168 @@ +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.Linq; +using System.Reflection; + +namespace Substrate.DotNet.Service.Node +{ + public class ArrayBuilderRoslyn : TypeBuilderBaseRoslyn + { + public static int Counter = 0; + + private ArrayBuilderRoslyn(string projectName, uint id, NodeTypeArray typeDef, NodeTypeResolver typeDict) + : base(projectName, id, typeDef, typeDict) + { + } + + private static MethodDeclarationSyntax GetDecodeRoslyn(string baseType) + { + 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 = 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 MethodDeclarationSyntax GetEncodeRoslyn() + { + 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; + } + + public static ArrayBuilderRoslyn Create(string projectName, uint id, NodeTypeArray nodeType, NodeTypeResolver typeDict) + { + return new ArrayBuilderRoslyn(projectName, id, nodeType, typeDict); + } + + public override TypeBuilderBaseRoslyn Create() + { + var typeDef = TypeDef as NodeTypeArray; + + NodeTypeResolved fullItem = GetFullItemPath(typeDef.TypeId); + + ClassName = $"Arr{typeDef.Length}{fullItem.ClassName}"; + + 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}"; + + 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); + + + 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 = targetClass.WithLeadingTrivia(GetCommentsRoslyn(typeDef.Docs, typeDef)); + + targetClass = targetClass.AddMembers(GetEncodeRoslyn(), GetDecodeRoslyn(fullItem.ToString())); + + 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); + + NamespaceDeclarationSyntax namespaceDeclaration = SyntaxFactory + .NamespaceDeclaration(SyntaxFactory.ParseName(NamespaceName)) + .AddMembers(targetClass); + + CompilationUnitSyntax compilationUnit = SyntaxFactory.CompilationUnit() + .AddMembers(namespaceDeclaration); + + TargetUnit = TargetUnit.AddMembers(compilationUnit.Members.ToArray()); + + return this; + } + + } +} \ No newline at end of file diff --git a/Tools/Substrate.DotNet/Service/Node/Base/BuilderBaseRoslyn.cs b/Tools/Substrate.DotNet/Service/Node/Base/BuilderBaseRoslyn.cs new file mode 100644 index 0000000..94ea284 --- /dev/null +++ b/Tools/Substrate.DotNet/Service/Node/Base/BuilderBaseRoslyn.cs @@ -0,0 +1,181 @@ +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; + +namespace Substrate.DotNet.Service.Node.Base +{ + public abstract class BuilderBaseRoslyn + { + protected static readonly List Files = new(); + + public uint Id { get; } + + private NodeTypeResolver Resolver { get; } + + public bool Success { get; set; } + + public string NamespaceName { get; protected set; } + + internal string FileName { get; set; } + + public string ClassName { get; set; } + + public string ReferenzName { get; set; } + + public string ProjectName { get; private set; } + + public NamespaceDeclarationSyntax ImportsNamespace { get; set; } + + public CompilationUnitSyntax TargetUnit { get; set; } + + public abstract BuilderBaseRoslyn Create(); + + protected BuilderBaseRoslyn(string projectName, uint id, NodeTypeResolver resolver) + { + ProjectName = projectName; + Id = id; + Resolver = resolver; + ImportsNamespace = SyntaxFactory.NamespaceDeclaration(SyntaxFactory.ParseName("Substrate.NetApi.Model.Types.Base")); + 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) + { + if (!Resolver.TypeNames.TryGetValue(typeId, out NodeTypeResolved fullItem)) + { + Success = false; + return null; + } + + return fullItem; + } + + public virtual void Build(bool write, out bool success, string basePath = null) + { + success = Success; + if (write && Success) + { + string path = GetPath(basePath); + + Directory.CreateDirectory(Path.GetDirectoryName(path)); + + if (!Files.Contains(path)) + { + Files.Add(path); + } + else + { + throw new IOException($"File {path} already exists."); + } + + // add autogenerated header + TargetUnit = TargetUnit.WithLeadingTrivia(SyntaxFactory.TriviaList(HeaderComment)); + + using StreamWriter sourceWriter = new(path); + sourceWriter.Write(TargetUnit.NormalizeWhitespace().ToFullString()); + } + } + + private string GetPath(string basePath) + { + var space = NamespaceName.Split('.').ToList(); + + 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. + space = space.TakeLast(space.Count - 2).ToList(); + + // Add base path at the beginning of the paths list + if (!string.IsNullOrEmpty(basePath)) + { + space.Insert(0, basePath); + } + + string path = Path.Combine(space.ToArray()); + + return path; + } + + protected ClassDeclarationSyntax AddTargetClassCustomAttributesRoslyn(ClassDeclarationSyntax targetClass, NodeType typeDef) + { + // TODO (svnscha): Change version to given metadata version. + TargetUnit = TargetUnit.AddUsings( + SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("Substrate.NetApi.Model.Types.Metadata.V14")), + SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("Substrate.NetApi.Attributes"))); + + 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) + { + commentList.Add(SyntaxFactory.Comment($"/// {doc}")); + } + } + + 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/TypeBuilderBaseRoslyn.cs b/Tools/Substrate.DotNet/Service/Node/Base/TypeBuilderBaseRoslyn.cs new file mode 100644 index 0000000..6ddf7ca --- /dev/null +++ b/Tools/Substrate.DotNet/Service/Node/Base/TypeBuilderBaseRoslyn.cs @@ -0,0 +1,16 @@ +using Substrate.NetApi.Model.Meta; + +namespace Substrate.DotNet.Service.Node.Base +{ + public abstract class TypeBuilderBaseRoslyn : BuilderBaseRoslyn + { + public NodeType TypeDef { get; } + + public TypeBuilderBaseRoslyn(string projectName, uint id, NodeType typeDef, NodeTypeResolver resolver) + : base(projectName, id, resolver) + { + TypeDef = typeDef; + NamespaceName = resolver.GetNamespace(id); + } + } +} \ No newline at end of file diff --git a/Tools/Substrate.DotNet/Service/Node/StructBuilderRoslyn.cs b/Tools/Substrate.DotNet/Service/Node/StructBuilderRoslyn.cs new file mode 100644 index 0000000..627625f --- /dev/null +++ b/Tools/Substrate.DotNet/Service/Node/StructBuilderRoslyn.cs @@ -0,0 +1,216 @@ +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.Collections.Generic; +using System.Linq; + +namespace Substrate.DotNet.Service.Node +{ + public class StructBuilderRoslyn : TypeBuilderBaseRoslyn + { + private StructBuilderRoslyn(string projectName, uint id, NodeTypeComposite typeDef, NodeTypeResolver typeDict) + : base(projectName, id, typeDef, typeDict) + { + } + + private static FieldDeclarationSyntax GetPropertyFieldRoslyn(string name, string baseType) + { + FieldDeclarationSyntax fieldDeclaration = SyntaxFactory.FieldDeclaration( + SyntaxFactory.VariableDeclaration( + SyntaxFactory.ParseTypeName(baseType), + SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.VariableDeclarator(name.MakePrivateField())))) + .AddModifiers(SyntaxFactory.Token(SyntaxKind.PrivateKeyword)); + + return fieldDeclaration; + } + + private static PropertyDeclarationSyntax GetPropertyWithFieldRoslyn(string name, FieldDeclarationSyntax propertyField) + { + 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 static PropertyDeclarationSyntax GetPropertyRoslyn(string name, TypeSyntax type) + { + 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) + { + for (int i = 0; i < typeFields.Length; i++) + { + NodeTypeField typeField = typeFields[i]; + + string fieldName = GetFieldName(typeField, "value", typeFields.Length, i); + NodeTypeResolved fullItem = GetFullItemPath(typeField.TypeId); + + decodeMethod = decodeMethod.AddBodyStatements(SyntaxFactory.ParseStatement($"{fieldName.MakeMethod()} = new {fullItem}();")); + decodeMethod = decodeMethod.AddBodyStatements(SyntaxFactory.ParseStatement($"{fieldName.MakeMethod()}.Decode(byteArray, ref p);")); + } + } + + 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 MethodDeclarationSyntax GetEncodeRoslyn(NodeTypeField[] typeFields) + { + 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) + { + for (int i = 0; i < typeFields.Length; i++) + { + NodeTypeField typeField = typeFields[i]; + string fieldName = StructBuilderRoslyn.GetFieldName(typeField, "value", typeFields.Length, i); + + encodeMethod = encodeMethod.AddBodyStatements(SyntaxFactory.ParseStatement($"result.AddRange({fieldName.MakeMethod()}.Encode());")); + } + } + + encodeMethod = encodeMethod.AddBodyStatements(SyntaxFactory.ParseStatement("return result.ToArray();")); + + return encodeMethod; + } + + public static BuilderBaseRoslyn Init(string projectName, uint id, NodeTypeComposite typeDef, NodeTypeResolver typeDict) + { + return new StructBuilderRoslyn(projectName, id, typeDef, typeDict); + } + + public override TypeBuilderBaseRoslyn Create() + { + var typeDef = TypeDef as NodeTypeComposite; + + ClassName = $"{typeDef.Path.Last()}"; + + ReferenzName = $"{NamespaceName}.{ClassName}"; + + 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 = 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 = GetFieldName(typeField, "value", typeDef.TypeFields.Length, i); + + NodeTypeResolved fullItem = GetFullItemPath(typeField.TypeId); + + //FieldDeclarationSyntax field = GetPropertyFieldRoslyn(fieldName, fullItem.ToString()); + // add comment to field if exists + //field = field.WithLeadingTrivia(GetCommentsRoslyn(typeField.Docs, null, fieldName)); + //targetClass = targetClass.AddMembers(field, GetPropertyWithFieldRoslyn(fieldName, field)); + + PropertyDeclarationSyntax propertyDeclaration = GetPropertyRoslyn(fieldName, SyntaxFactory.ParseTypeName(fullItem.ToString())); + propertyDeclaration = propertyDeclaration.WithLeadingTrivia(GetCommentsRoslyn(typeField.Docs, null, fieldName)); + + targetClass = targetClass.AddMembers(propertyDeclaration); + } + } + + 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); + + TargetUnit = TargetUnit.AddMembers(compilationUnit.Members.ToArray()); + + return this; + } + + private static string GetFieldName(NodeTypeField typeField, string alterName, int length, int index) + { + if (typeField.Name == null) + { + if (length > 1) + { + if (typeField.TypeName == null) + { + return alterName + index; + } + else + { + return typeField.TypeName; + } + } + else + { + return alterName; + } + } + else + { + return typeField.Name; + } + } + } +} \ 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 @@ + From 58678694435b4ec25110f0e917a7cf86215f10e0 Mon Sep 17 00:00:00 2001 From: "cedric.decoster" Date: Mon, 29 May 2023 18:42:52 +0200 Subject: [PATCH 03/14] added roslyn enumBuilder fixed big overwriting issue --- .../Generators/Base/SolutionGeneratorBase.cs | 6 +- .../Service/Node/ArrayBuilderRoslyn.cs | 6 - .../Service/Node/Base/BuilderBaseRoslyn.cs | 13 +- .../Service/Node/EnumBuilderRoslyn.cs | 127 ++++++++++++++++++ .../Service/Node/NodeTypeResolver.cs | 45 +++++-- 5 files changed, 175 insertions(+), 22 deletions(-) create mode 100644 Tools/Substrate.DotNet/Service/Node/EnumBuilderRoslyn.cs diff --git a/Tools/Substrate.DotNet/Service/Generators/Base/SolutionGeneratorBase.cs b/Tools/Substrate.DotNet/Service/Generators/Base/SolutionGeneratorBase.cs index 49a2660..7f05d29 100644 --- a/Tools/Substrate.DotNet/Service/Generators/Base/SolutionGeneratorBase.cs +++ b/Tools/Substrate.DotNet/Service/Generators/Base/SolutionGeneratorBase.cs @@ -79,7 +79,7 @@ protected NodeTypeResolver GenerateTypes(Dictionary nodeTypes, s case TypeDefEnum.Array: { var type = nodeType as NodeTypeArray; - ArrayBuilderRoslyn.Create(ProjectName, type.Id, type, resolver) + ArrayBuilder.Create(ProjectName, type.Id, type, resolver) .Create() .Build(write: write, out bool success, basePath); @@ -95,6 +95,8 @@ protected NodeTypeResolver GenerateTypes(Dictionary nodeTypes, s } } + + return resolver; } @@ -113,7 +115,7 @@ private void CallVariant(string variantType, NodeTypeVariant nodeType, ref NodeT } case "Enum": { - EnumBuilder.Init(ProjectName, nodeType.Id, nodeType, typeDict).Create().Build(write: write, out bool success, basePath); + EnumBuilderRoslyn.Init(ProjectName, nodeType.Id, nodeType, typeDict).Create().Build(write: write, out bool success, basePath); if (!success) { Logger.Error($"Could not build type {nodeType.Id}!"); diff --git a/Tools/Substrate.DotNet/Service/Node/ArrayBuilderRoslyn.cs b/Tools/Substrate.DotNet/Service/Node/ArrayBuilderRoslyn.cs index 2c03b1e..aa38d83 100644 --- a/Tools/Substrate.DotNet/Service/Node/ArrayBuilderRoslyn.cs +++ b/Tools/Substrate.DotNet/Service/Node/ArrayBuilderRoslyn.cs @@ -3,10 +3,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Substrate.DotNet.Service.Node.Base; using Substrate.NetApi.Model.Meta; -using System; -using System.CodeDom; using System.Linq; -using System.Reflection; namespace Substrate.DotNet.Service.Node { @@ -111,7 +108,6 @@ public override TypeBuilderBaseRoslyn Create() SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))); targetClass = targetClass.AddMembers(valueProperty); - ReturnStatementSyntax typeNameReturn = SyntaxFactory.ReturnStatement( SyntaxFactory.InvocationExpression( SyntaxFactory.MemberAccessExpression( @@ -136,7 +132,6 @@ public override TypeBuilderBaseRoslyn Create() .WithBody(SyntaxFactory.Block(typeNameReturn)); targetClass = targetClass.AddMembers(nameMethod); - targetClass = AddTargetClassCustomAttributesRoslyn(targetClass, typeDef); // add comment to class if exists targetClass = targetClass.WithLeadingTrivia(GetCommentsRoslyn(typeDef.Docs, typeDef)); @@ -163,6 +158,5 @@ public override TypeBuilderBaseRoslyn Create() return this; } - } } \ No newline at end of file diff --git a/Tools/Substrate.DotNet/Service/Node/Base/BuilderBaseRoslyn.cs b/Tools/Substrate.DotNet/Service/Node/Base/BuilderBaseRoslyn.cs index 94ea284..0440f7c 100644 --- a/Tools/Substrate.DotNet/Service/Node/Base/BuilderBaseRoslyn.cs +++ b/Tools/Substrate.DotNet/Service/Node/Base/BuilderBaseRoslyn.cs @@ -5,6 +5,8 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Substrate.NetApi.Model.Meta; +using System; +using Serilog; namespace Substrate.DotNet.Service.Node.Base { @@ -14,7 +16,7 @@ public abstract class BuilderBaseRoslyn public uint Id { get; } - private NodeTypeResolver Resolver { get; } + protected NodeTypeResolver Resolver { get; } public bool Success { get; set; } @@ -73,7 +75,7 @@ public virtual void Build(bool write, out bool success, string basePath = null) } else { - throw new IOException($"File {path} already exists."); + Log.Warning($"Overwriting[BUG]: {path}"); } // add autogenerated header @@ -158,7 +160,12 @@ public static SyntaxTriviaList GetCommentsRoslyn(string[] docs, NodeType typeDef { foreach (string doc in docs) { - commentList.Add(SyntaxFactory.Comment($"/// {doc}")); + string[] lines = doc.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None); + + foreach (string line in lines) + { + commentList.Add(SyntaxFactory.Comment($"/// {line}")); + } } } diff --git a/Tools/Substrate.DotNet/Service/Node/EnumBuilderRoslyn.cs b/Tools/Substrate.DotNet/Service/Node/EnumBuilderRoslyn.cs new file mode 100644 index 0000000..c743999 --- /dev/null +++ b/Tools/Substrate.DotNet/Service/Node/EnumBuilderRoslyn.cs @@ -0,0 +1,127 @@ +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.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 +{ + public class EnumBuilderRoslyn : TypeBuilderBaseRoslyn + { + private EnumBuilderRoslyn(string projectName, uint id, NodeTypeVariant typeDef, NodeTypeResolver typeDict) + : base(projectName, id, typeDef, typeDict) + { + } + + public static EnumBuilderRoslyn Init(string projectName, uint id, NodeTypeVariant typeDef, NodeTypeResolver typeDict) + { + return new EnumBuilderRoslyn(projectName, id, typeDef, typeDict); + } + + public override TypeBuilderBaseRoslyn Create() + { + var typeDef = TypeDef as NodeTypeVariant; + string enumName = $"{typeDef.Path.Last()}"; + + if (!Resolver.TypeNames.TryGetValue(Id, out NodeTypeResolved nodeTypeResolved)) + { + throw new NotSupportedException($"Could not find type {Id}"); + } + + ClassName = nodeTypeResolved.ClassName; + + ReferenzName = $"{NamespaceName}.{ClassName}"; + + NamespaceDeclarationSyntax typeNamespace = SyntaxFactory.NamespaceDeclaration(SyntaxFactory.ParseName(NamespaceName)); + + // only add the enumeration in the first variations. + if (string.IsNullOrEmpty(nodeTypeResolved.Name.ClassNameSufix) || nodeTypeResolved.Name.ClassNameSufix == "1") + { + EnumDeclarationSyntax targetType = SyntaxFactory + .EnumDeclaration(enumName) + .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)); + + if (typeDef.Variants != null) + { + foreach (TypeVariant variant in typeDef.Variants) + { + targetType = targetType.AddMembers( + SyntaxFactory.EnumMemberDeclaration(variant.Name) + .WithEqualsValue(SyntaxFactory.EqualsValueClause(SyntaxFactory.LiteralExpression(SyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal(variant.Index)))) + ); + } + } + typeNamespace = typeNamespace.AddMembers(targetType); + } + + + 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 = targetClass.AddBaseListTypes(SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName($"BaseEnum<{enumName}>"))); + typeNamespace = typeNamespace.AddMembers(targetClass); + } + else + { + 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.FirstOrDefault(p => p.Index == i); + + if (variant == null || variant.TypeFields == null) + { + // add void type + genericTypeArguments.Add(SyntaxFactory.ParseTypeName("BaseVoid")); + } + else + { + if (variant.TypeFields.Length == 1) + { + NodeTypeResolved item = GetFullItemPath(variant.TypeFields[0].TypeId); + genericTypeArguments.Add(SyntaxFactory.ParseTypeName(item.ToString())); + } + else + { + var baseTupleArgs = new List(); + foreach (NodeTypeField field in variant.TypeFields) + { + NodeTypeResolved item = GetFullItemPath(field.TypeId); + baseTupleArgs.Add(SyntaxFactory.ParseTypeName(item.ToString())); + } + GenericNameSyntax baseTuple = SyntaxFactory.GenericName(SyntaxFactory.Identifier("BaseTuple"), SyntaxFactory.TypeArgumentList(SyntaxFactory.SeparatedList(baseTupleArgs))); + genericTypeArguments.Add(baseTuple); + } + } + } + } + else + { + throw new NotImplementedException("Enum extension can't handle such big sized typed rust enumeration, please create a manual fix for it."); + } + + GenericNameSyntax baseEnumExt = SyntaxFactory.GenericName(SyntaxFactory.Identifier("BaseEnumExt"), SyntaxFactory.TypeArgumentList(SyntaxFactory.SeparatedList(genericTypeArguments))); + targetClass = targetClass.AddBaseListTypes(SyntaxFactory.SimpleBaseType(baseEnumExt)); + typeNamespace = typeNamespace.AddMembers(targetClass); + } + + TargetUnit = TargetUnit.AddMembers(typeNamespace); + + return this; + } + } +} \ 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 2ab9454..d1ff6eb 100644 --- a/Tools/Substrate.DotNet/Service/Node/NodeTypeResolver.cs +++ b/Tools/Substrate.DotNet/Service/Node/NodeTypeResolver.cs @@ -1,5 +1,6 @@ #nullable enable +using Serilog; using Substrate.DotNet.Extensions; using Substrate.NetApi.Model.Meta; using Substrate.NetApi.Model.Types.Metadata.V14; @@ -7,6 +8,7 @@ using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; +using System.Xml.Linq; namespace Substrate.DotNet.Service.Node { @@ -39,8 +41,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 @@ -115,20 +118,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) { @@ -157,10 +152,38 @@ 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); + + } + + // 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; From 80fc5de927c7feb61c2b7113213e96678b402f44 Mon Sep 17 00:00:00 2001 From: "cedric.decoster" Date: Mon, 29 May 2023 22:24:59 +0200 Subject: [PATCH 04/14] wip ModuleGenBuilderRoslyn --- .../Generators/Base/SolutionGeneratorBase.cs | 25 +- .../Service/Generators/NetApiGenerator.cs | 2 +- .../Service/Node/Base/BuilderBaseRoslyn.cs | 12 + .../Node/Base/ModuleBuilderBaseRoslyn.cs | 25 + .../Service/Node/ModuleGenBuilder.cs | 32 +- .../Service/Node/ModuleGenBuilderRoslyn.cs | 606 ++++++++++++++++++ .../Service/Node/NodeTypeResolver.cs | 7 +- .../Service/Node/RunetimeBuilder.cs | 63 -- 8 files changed, 666 insertions(+), 106 deletions(-) create mode 100644 Tools/Substrate.DotNet/Service/Node/Base/ModuleBuilderBaseRoslyn.cs create mode 100644 Tools/Substrate.DotNet/Service/Node/ModuleGenBuilderRoslyn.cs delete mode 100644 Tools/Substrate.DotNet/Service/Node/RunetimeBuilder.cs diff --git a/Tools/Substrate.DotNet/Service/Generators/Base/SolutionGeneratorBase.cs b/Tools/Substrate.DotNet/Service/Generators/Base/SolutionGeneratorBase.cs index 7f05d29..acb5f7d 100644 --- a/Tools/Substrate.DotNet/Service/Generators/Base/SolutionGeneratorBase.cs +++ b/Tools/Substrate.DotNet/Service/Generators/Base/SolutionGeneratorBase.cs @@ -94,9 +94,6 @@ protected NodeTypeResolver GenerateTypes(Dictionary nodeTypes, s break; // Handled by type resolver } } - - - return resolver; } @@ -104,18 +101,9 @@ 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": { - EnumBuilderRoslyn.Init(ProjectName, nodeType.Id, nodeType, typeDict).Create().Build(write: write, out bool success, basePath); + EnumBuilder.Init(ProjectName, nodeType.Id, nodeType, typeDict).Create().Build(write: write, out bool success, basePath); if (!success) { Logger.Error($"Could not build type {nodeType.Id}!"); @@ -123,8 +111,17 @@ 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}"); } } diff --git a/Tools/Substrate.DotNet/Service/Generators/NetApiGenerator.cs b/Tools/Substrate.DotNet/Service/Generators/NetApiGenerator.cs index c13068a..bfd150d 100644 --- a/Tools/Substrate.DotNet/Service/Generators/NetApiGenerator.cs +++ b/Tools/Substrate.DotNet/Service/Generators/NetApiGenerator.cs @@ -40,7 +40,7 @@ private static void GenerateModules(string projectName, Dictionary modulesResolved = new(); foreach (PalletModule module in modules.Values) { - ModuleGenBuilder + ModuleGenBuilderRoslyn .Init(projectName, module.Index, module, typeDict, nodeTypes) .Create() .Build(write: true, out bool _, basePath); diff --git a/Tools/Substrate.DotNet/Service/Node/Base/BuilderBaseRoslyn.cs b/Tools/Substrate.DotNet/Service/Node/Base/BuilderBaseRoslyn.cs index 0440f7c..09ba059 100644 --- a/Tools/Substrate.DotNet/Service/Node/Base/BuilderBaseRoslyn.cs +++ b/Tools/Substrate.DotNet/Service/Node/Base/BuilderBaseRoslyn.cs @@ -7,6 +7,7 @@ using Substrate.NetApi.Model.Meta; using System; using Serilog; +using System.Net.NetworkInformation; namespace Substrate.DotNet.Service.Node.Base { @@ -49,6 +50,17 @@ protected BuilderBaseRoslyn(string projectName, uint id, NodeTypeResolver resolv Success = true; } + public static string EscapeIfKeyword(string parameterName) + { + if (SyntaxFacts.GetKeywordKind(parameterName) != SyntaxKind.None) + { + // If it is a keyword, create a verbatim identifier which adds '@' prefix + parameterName = "@" + parameterName; + } + + return parameterName; + } + public NodeTypeResolved GetFullItemPath(uint typeId) { if (!Resolver.TypeNames.TryGetValue(typeId, out NodeTypeResolved fullItem)) diff --git a/Tools/Substrate.DotNet/Service/Node/Base/ModuleBuilderBaseRoslyn.cs b/Tools/Substrate.DotNet/Service/Node/Base/ModuleBuilderBaseRoslyn.cs new file mode 100644 index 0000000..53f545c --- /dev/null +++ b/Tools/Substrate.DotNet/Service/Node/Base/ModuleBuilderBaseRoslyn.cs @@ -0,0 +1,25 @@ +using Substrate.DotNet.Extensions; +using Substrate.NetApi.Model.Meta; +using System.Collections.Generic; + +namespace Substrate.DotNet.Service.Node.Base +{ + public abstract class ModuleBuilderBaseRoslyn : BuilderBaseRoslyn + { + public Dictionary NodeTypes { get; private set; } + + public PalletModule Module { get; private set; } + + public string PrefixName { get; private set; } + + protected ModuleBuilderBaseRoslyn(string projectName, uint id, PalletModule module, NodeTypeResolver typeDict, + Dictionary nodeTypes) + : base(projectName, id, typeDict) + { + NodeTypes = nodeTypes; + Module = module; + PrefixName = module.Name == "System" ? "Frame" : "Pallet"; + NamespaceName = $"{ProjectName}.Generated.Model.{PrefixName + module.Name.MakeMethod()}"; + } + } +} \ 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..793fe9f 100644 --- a/Tools/Substrate.DotNet/Service/Node/ModuleGenBuilder.cs +++ b/Tools/Substrate.DotNet/Service/Node/ModuleGenBuilder.cs @@ -136,7 +136,7 @@ private void CreateStorage(CodeNamespace typeNamespace, CodeConstructor construc NodeTypeResolved fullItem = GetFullItemPath(entry.TypeMap.Item1); parameterMethod.Statements.Add(new CodeMethodReturnStatement( - ModuleGenBuilder.GetStorageString(storage.Prefix, entry.Name, entry.StorageType))); + GetStorageString(storage.Prefix, entry.Name, entry.StorageType))); storageMethod.ReturnType = new CodeTypeReference($"async Task<{fullItem}>"); @@ -150,7 +150,7 @@ private void CreateStorage(CodeNamespace typeNamespace, CodeConstructor construc storageMethod.Statements.Add(variableDeclaration1); // create result - var resultStatement = new CodeArgumentReferenceExpression(ModuleGenBuilder.GetInvoceString(fullItem.ToString())); + var resultStatement = new CodeArgumentReferenceExpression(GetInvoceString(fullItem.ToString())); CodeVariableDeclarationStatement variableDeclaration2 = new("var", "result", resultStatement); storageMethod.Statements.Add(variableDeclaration2); @@ -161,7 +161,7 @@ private void CreateStorage(CodeNamespace typeNamespace, CodeConstructor construc // add storage key mapping in constructor constructor.Statements.Add( - ModuleGenBuilder.AddPropertyValues(ModuleGenBuilder.GetStorageMapString("", fullItem.ToString(), storage.Prefix, entry.Name), "_client.StorageKeyDict")); + AddPropertyValues(GetStorageMapString("", fullItem.ToString(), storage.Prefix, entry.Name), "_client.StorageKeyDict")); } else if (entry.StorageType == Storage.Type.Map) { @@ -172,38 +172,24 @@ private void CreateStorage(CodeNamespace typeNamespace, CodeConstructor construc parameterMethod.Parameters.Add(new CodeParameterDeclarationExpression(key.ToString(), "key")); parameterMethod.Statements.Add(new CodeMethodReturnStatement( - ModuleGenBuilder.GetStorageString(storage.Prefix, entry.Name, entry.StorageType, hashers))); + 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") }); + CodeMethodInvokeExpression methodInvoke = new( + new CodeTypeReferenceExpression(targetClass.Name), + parameterMethod.Name, + new CodeArgumentReferenceExpression("key")); CodeVariableDeclarationStatement variableDeclaration = new(typeof(string), "parameters", methodInvoke); storageMethod.Statements.Add(variableDeclaration); // create result - var resultStatement = new CodeArgumentReferenceExpression(ModuleGenBuilder.GetInvoceString(value.ToString())); + var resultStatement = new CodeArgumentReferenceExpression(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); - //} // return statement storageMethod.Statements.Add( diff --git a/Tools/Substrate.DotNet/Service/Node/ModuleGenBuilderRoslyn.cs b/Tools/Substrate.DotNet/Service/Node/ModuleGenBuilderRoslyn.cs new file mode 100644 index 0000000..f912d60 --- /dev/null +++ b/Tools/Substrate.DotNet/Service/Node/ModuleGenBuilderRoslyn.cs @@ -0,0 +1,606 @@ +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 System; +using System.Collections.Generic; +using System.Linq; + +namespace Substrate.DotNet.Service.Node +{ + public class ModuleGenBuilderRoslyn : ModuleBuilderBaseRoslyn + { + private ModuleGenBuilderRoslyn(string projectName, uint id, PalletModule module, NodeTypeResolver typeDict, Dictionary nodeTypes) : + base(projectName, id, module, typeDict, nodeTypes) + { + } + + public static ModuleGenBuilderRoslyn Init(string projectName, uint id, PalletModule module, NodeTypeResolver typeDict, Dictionary nodeTypes) + { + return new ModuleGenBuilderRoslyn(projectName, id, module, typeDict, nodeTypes); + } + + public override ModuleGenBuilderRoslyn Create() + { + 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; + + NamespaceDeclarationSyntax typeNamespace = SyntaxFactory + .NamespaceDeclaration(SyntaxFactory.ParseName(NamespaceName)); + + typeNamespace = CreateStorage(typeNamespace); + typeNamespace = CreateCalls(typeNamespace); + typeNamespace = CreateEvents(typeNamespace); + typeNamespace = CreateConstants(typeNamespace); + typeNamespace = CreateErrors(typeNamespace); + + TargetUnit = TargetUnit + .AddUsings(usings) + .AddMembers(typeNamespace); + + return this; + } + + private NamespaceDeclarationSyntax CreateStorage(NamespaceDeclarationSyntax typeNamespace) + { + ClassName = Module.Name + "Storage"; + + PalletStorage storage = Module.Storage; + + 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(SyntaxFactory.Comment("// Substrate client for the storage calls.")); + targetClass = targetClass.AddMembers(clientField); + + // Add parameters. + 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"; + + // 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 + + MethodDeclarationSyntax storageMethod; + + string returnTypeStr = string.Empty; + + if (entry.StorageType == Storage.Type.Plain) + { + NodeTypeResolved fullItem = GetFullItemPath(entry.TypeMap.Item1); + + storageMethod = SyntaxFactory + .MethodDeclaration(SyntaxFactory.ParseTypeName($"Task<{fullItem}>"), entry.Name); + + parameterMethod = parameterMethod.AddBodyStatements( + SyntaxFactory.ReturnStatement(GetStorageStringRoslyn(storage.Prefix, entry.Name, entry.StorageType))); + + // add storage key mapping in constructor + constructor = constructor.AddBodyStatements(AddPropertyValuesRoslyn(GetStorageMapStringRoslyn("", fullItem.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); + + storageMethod = SyntaxFactory + .MethodDeclaration(SyntaxFactory.ParseTypeName($"Task<{value}>"), 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()))); + + // add storage key mapping in constructor + constructor = constructor.AddBodyStatements(AddPropertyValuesRoslyn(GetStorageMapStringRoslyn(key.ToString(), value.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"))); + + ExpressionStatementSyntax methodInvoke = SyntaxFactory.ExpressionStatement(SyntaxFactory.InvocationExpression(SyntaxFactory.IdentifierName(parameterMethod.Identifier))); + + 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(returnTypeStr.Replace("Task<", "").Replace(">", "")); + 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 NamespaceDeclarationSyntax CreateCalls(NamespaceDeclarationSyntax namespaceDeclaration) + { + ClassName = Module.Name + "Calls"; + + PalletCalls calls = Module.Calls; + + ClassDeclarationSyntax targetClass = SyntaxFactory.ClassDeclaration(ClassName) + .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.SealedKeyword))); + + if (calls != null) + { + if (NodeTypes.TryGetValue(calls.TypeId, out NodeType nodeType)) + { + var typeDef = nodeType as NodeTypeVariant; + + if (typeDef.Variants != null) + { + foreach (TypeVariant variant in typeDef.Variants) + { + 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 = callMethod.WithLeadingTrivia(GetCommentsRoslyn(typeDef.Docs, null, variant.Name)); + + string byteArrayName = "byteArray"; + + 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) + { + foreach (NodeTypeField field in variant.TypeFields) + { + NodeTypeResolved fullItem = GetFullItemPath(field.TypeId); + + // 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 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 NamespaceDeclarationSyntax CreateEvents(NamespaceDeclarationSyntax namespaceDeclaration) + { + ClassName = Module.Name + "Events"; + + PalletEvents events = Module.Events; + + //if (events != null && NodeTypes.TryGetValue(events.TypeId, out NodeType nodeType)) + //{ + // var typeDef = nodeType as NodeTypeVariant; + + // if (typeDef.Variants != null) + // { + // foreach (TypeVariant variant in typeDef.Variants) + // { + // 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)); + + // QualifiedNameSyntax baseTupleType = SyntaxFactory.QualifiedName(SyntaxFactory.IdentifierName("BaseTuple"), SyntaxFactory.IdentifierName(string.Empty)); + // if (variant.TypeFields != null) + // { + // foreach (NodeTypeField field in variant.TypeFields) + // { + // NodeTypeResolved fullItem = GetFullItemPath(field.TypeId); + // baseTupleType = baseTupleType.WithRight(SyntaxFactory.IdentifierName(fullItem.ToString())); + // } + // } + // eventClass = eventClass.AddBaseListTypes(SyntaxFactory.SimpleBaseType(baseTupleType)); + + // namespaceDeclaration = namespaceDeclaration.AddMembers(eventClass); + // } + // } + //} + + return namespaceDeclaration; + } + + private NamespaceDeclarationSyntax CreateConstants(NamespaceDeclarationSyntax namespaceDeclaration) + { + ClassName = Module.Name + "Constants"; + + PalletConstant[] constants = Module.Constants; + + 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) + { + MethodDeclarationSyntax constantMethod = SyntaxFactory.MethodDeclaration(SyntaxFactory.ParseTypeName("void"), constant.Name) + .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword))); + + // add comment to class if exists + constantMethod = constantMethod.WithLeadingTrivia(GetCommentsRoslyn(constant.Docs, null, constant.Name)); + + targetClass = targetClass.AddMembers(constantMethod); + + if (NodeTypes.TryGetValue(constant.TypeId, out NodeType nodeType)) + { + NodeTypeResolved nodeTypeResolved = GetFullItemPath(nodeType.Id); + constantMethod = constantMethod.WithReturnType(SyntaxFactory.ParseTypeName(nodeTypeResolved.ToString())); + + // assign new result object + 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 + 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 = 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 NamespaceDeclarationSyntax CreateErrors(NamespaceDeclarationSyntax namespaceDeclaration) + { + ClassName = Module.Name + "Errors"; + + PalletErrors errors = Module.Errors; + + if (errors != null) + { + if (NodeTypes.TryGetValue(errors.TypeId, out NodeType nodeType)) + { + var typeDef = nodeType as NodeTypeVariant; + + EnumDeclarationSyntax targetClass = SyntaxFactory.EnumDeclaration(ClassName) + .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword))); + + if (typeDef.Variants != null) + { + foreach (TypeVariant variant in typeDef.Variants) + { + EnumMemberDeclarationSyntax enumField = SyntaxFactory.EnumMemberDeclaration(variant.Name); + + // add comment to field if exists + enumField = enumField.WithLeadingTrivia(GetCommentsRoslyn(variant.Docs, null, variant.Name)); + + targetClass = targetClass.AddMembers(enumField); + } + } + + namespaceDeclaration = namespaceDeclaration.AddMembers(targetClass); + } + } + + return namespaceDeclaration; + } + + private static string GetInvoceString(string returnType) + { + return "await _client.GetStorageAsync<" + returnType + ">(parameters, token)"; + } + + private static InvocationExpressionSyntax GetStorageStringRoslyn(string module, string item, Storage.Type type, Storage.Hasher[] hashers = null) + { + 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) + { + ExpressionSyntax keyReference = SyntaxFactory.ArrayCreationExpression( + SyntaxFactory.ArrayType( + SyntaxFactory.IdentifierName("IType"), + SyntaxFactory.SingletonList( + SyntaxFactory.ArrayRankSpecifier( + SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.OmittedArraySizeExpression())))), + SyntaxFactory.InitializerExpression(SyntaxKind.ArrayInitializerExpression, + SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.IdentifierName("key")))); + + if (hashers.Length > 1) + { + keyReference = SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.IdentifierName("key"), + SyntaxFactory.IdentifierName("Value") + ); + } + + 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 SyntaxFactory.InvocationExpression( + SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.IdentifierName("RequestGenerator"), + SyntaxFactory.IdentifierName("GetStorage"))) + .WithArgumentList(SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(codeExpressions))); + } + + private static ExpressionSyntax[] GetStorageMapStringRoslyn(string keyType, string returnType, string module, string item, Storage.Hasher[] hashers = null) + { + 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) + { + 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 ExpressionStatementSyntax AddPropertyValuesRoslyn(ExpressionSyntax[] exprs, string variableReference) + { + 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 d1ff6eb..77e7a90 100644 --- a/Tools/Substrate.DotNet/Service/Node/NodeTypeResolver.cs +++ b/Tools/Substrate.DotNet/Service/Node/NodeTypeResolver.cs @@ -8,7 +8,6 @@ using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; -using System.Xml.Linq; namespace Substrate.DotNet.Service.Node { @@ -165,14 +164,13 @@ private Dictionary Resolve(Dictionary ty dupeCountDict[key] = new List(); } dupeCountDict[key].Add(typeId); - } // adding an index ass classname suffix to avoid overwriting - foreach(List entries in dupeCountDict.Values.Where(p => p.Count > 1)) + foreach (List entries in dupeCountDict.Values.Where(p => p.Count > 1)) { int index = 0; - foreach(uint typeId in entries) + foreach (uint typeId in entries) { index++; NodeTypeResolved nodeTypeResolved = result[typeId]; @@ -183,7 +181,6 @@ private Dictionary Resolve(Dictionary ty result[typeId] = nodeTypeResolved; } } - } return result; diff --git a/Tools/Substrate.DotNet/Service/Node/RunetimeBuilder.cs b/Tools/Substrate.DotNet/Service/Node/RunetimeBuilder.cs deleted file mode 100644 index ab98dad..0000000 --- a/Tools/Substrate.DotNet/Service/Node/RunetimeBuilder.cs +++ /dev/null @@ -1,63 +0,0 @@ -using Substrate.DotNet.Service.Node.Base; -using Substrate.NetApi.Model.Meta; -using System.CodeDom; -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; - } - } -} \ No newline at end of file From 1922327ce747b4862c2a67620929bf6def3d41dc Mon Sep 17 00:00:00 2001 From: "cedric.decoster" Date: Mon, 29 May 2023 23:07:37 +0200 Subject: [PATCH 05/14] wip not much missing --- .../Generators/Base/SolutionGeneratorBase.cs | 2 +- .../Service/Node/EnumBuilderRoslyn.cs | 2 +- .../Service/Node/ModuleGenBuilderRoslyn.cs | 81 ++++++++++--------- 3 files changed, 45 insertions(+), 40 deletions(-) diff --git a/Tools/Substrate.DotNet/Service/Generators/Base/SolutionGeneratorBase.cs b/Tools/Substrate.DotNet/Service/Generators/Base/SolutionGeneratorBase.cs index acb5f7d..83ea5b0 100644 --- a/Tools/Substrate.DotNet/Service/Generators/Base/SolutionGeneratorBase.cs +++ b/Tools/Substrate.DotNet/Service/Generators/Base/SolutionGeneratorBase.cs @@ -103,7 +103,7 @@ private void CallVariant(string variantType, NodeTypeVariant nodeType, ref NodeT { case "Enum": { - EnumBuilder.Init(ProjectName, nodeType.Id, nodeType, typeDict).Create().Build(write: write, out bool success, basePath); + EnumBuilderRoslyn.Init(ProjectName, nodeType.Id, nodeType, typeDict).Create().Build(write: write, out bool success, basePath); if (!success) { Logger.Error($"Could not build type {nodeType.Id}!"); diff --git a/Tools/Substrate.DotNet/Service/Node/EnumBuilderRoslyn.cs b/Tools/Substrate.DotNet/Service/Node/EnumBuilderRoslyn.cs index c743999..b7699dd 100644 --- a/Tools/Substrate.DotNet/Service/Node/EnumBuilderRoslyn.cs +++ b/Tools/Substrate.DotNet/Service/Node/EnumBuilderRoslyn.cs @@ -54,7 +54,7 @@ public override TypeBuilderBaseRoslyn Create() foreach (TypeVariant variant in typeDef.Variants) { targetType = targetType.AddMembers( - SyntaxFactory.EnumMemberDeclaration(variant.Name) + SyntaxFactory.EnumMemberDeclaration(EscapeIfKeyword(variant.Name)) .WithEqualsValue(SyntaxFactory.EqualsValueClause(SyntaxFactory.LiteralExpression(SyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal(variant.Index)))) ); } diff --git a/Tools/Substrate.DotNet/Service/Node/ModuleGenBuilderRoslyn.cs b/Tools/Substrate.DotNet/Service/Node/ModuleGenBuilderRoslyn.cs index f912d60..b1ef41b 100644 --- a/Tools/Substrate.DotNet/Service/Node/ModuleGenBuilderRoslyn.cs +++ b/Tools/Substrate.DotNet/Service/Node/ModuleGenBuilderRoslyn.cs @@ -107,31 +107,33 @@ private NamespaceDeclarationSyntax CreateStorage(NamespaceDeclarationSyntax type .WithLeadingTrivia(GetCommentsRoslyn(entry.Docs, null, storageParams)); // Assuming GetComments() returns a string MethodDeclarationSyntax storageMethod; - - string returnTypeStr = string.Empty; + ExpressionStatementSyntax methodInvoke; + NodeTypeResolved returnValueStr; if (entry.StorageType == Storage.Type.Plain) { - NodeTypeResolved fullItem = GetFullItemPath(entry.TypeMap.Item1); + returnValueStr = GetFullItemPath(entry.TypeMap.Item1); storageMethod = SyntaxFactory - .MethodDeclaration(SyntaxFactory.ParseTypeName($"Task<{fullItem}>"), entry.Name); + .MethodDeclaration(SyntaxFactory.ParseTypeName($"Task<{returnValueStr}>"), entry.Name); parameterMethod = parameterMethod.AddBodyStatements( SyntaxFactory.ReturnStatement(GetStorageStringRoslyn(storage.Prefix, entry.Name, entry.StorageType))); + methodInvoke = SyntaxFactory.ExpressionStatement(SyntaxFactory.InvocationExpression(SyntaxFactory.IdentifierName(parameterMethod.Identifier))); + // add storage key mapping in constructor - constructor = constructor.AddBodyStatements(AddPropertyValuesRoslyn(GetStorageMapStringRoslyn("", 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); + returnValueStr = GetFullItemPath(typeMap.Value); storageMethod = SyntaxFactory - .MethodDeclaration(SyntaxFactory.ParseTypeName($"Task<{value}>"), entry.Name); + .MethodDeclaration(SyntaxFactory.ParseTypeName($"Task<{returnValueStr}>"), entry.Name); parameterMethod = parameterMethod .AddParameterListParameters(SyntaxFactory.Parameter(SyntaxFactory.Identifier("key")) @@ -142,8 +144,13 @@ private NamespaceDeclarationSyntax CreateStorage(NamespaceDeclarationSyntax type .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 = constructor.AddBodyStatements(AddPropertyValuesRoslyn(GetStorageMapStringRoslyn(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 { @@ -152,20 +159,18 @@ private NamespaceDeclarationSyntax CreateStorage(NamespaceDeclarationSyntax type 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 - + .WithLeadingTrivia(GetCommentsRoslyn(entry.Docs, null, entry.Name)); // Assuming GetComments() returns a string storageMethod = storageMethod .AddParameterListParameters(SyntaxFactory.Parameter(SyntaxFactory.Identifier("token")).WithType(SyntaxFactory.ParseTypeName("CancellationToken"))); - ExpressionStatementSyntax methodInvoke = SyntaxFactory.ExpressionStatement(SyntaxFactory.InvocationExpression(SyntaxFactory.IdentifierName(parameterMethod.Identifier))); - 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(returnTypeStr.Replace("Task<", "").Replace(">", "")); + string resultString = GetInvoceString(returnValueStr.ToString()); + VariableDeclarationSyntax variableDeclaration2 = SyntaxFactory .VariableDeclaration(SyntaxFactory.IdentifierName("var")) .AddVariables(SyntaxFactory.VariableDeclarator(SyntaxFactory.Identifier("result"), null, SyntaxFactory.EqualsValueClause(SyntaxFactory.IdentifierName(resultString)))); @@ -228,7 +233,7 @@ private NamespaceDeclarationSyntax CreateCalls(NamespaceDeclarationSyntax namesp { MethodDeclarationSyntax callMethod = SyntaxFactory .MethodDeclaration(SyntaxFactory.ParseTypeName(nameof(Method)), variant.Name.MakeMethod()) - .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword))) + .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword))) .WithBody(SyntaxFactory.Block()); // add comment to class if exists @@ -481,7 +486,7 @@ private static InvocationExpressionSyntax GetStorageStringRoslyn(string module, { ExpressionSyntax keyReference = SyntaxFactory.ArrayCreationExpression( SyntaxFactory.ArrayType( - SyntaxFactory.IdentifierName("IType"), + SyntaxFactory.IdentifierName("Substrate.NetApi.Model.Types.IType"), SyntaxFactory.SingletonList( SyntaxFactory.ArrayRankSpecifier( SyntaxFactory.SingletonSeparatedList( @@ -533,39 +538,39 @@ private static ExpressionSyntax[] GetStorageMapStringRoslyn(string keyType, stri 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) - }; + 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) { ArrayCreationExpressionSyntax arrayExpression = SyntaxFactory.ArrayCreationExpression( - SyntaxFactory.ArrayType(SyntaxFactory.IdentifierName("Substrate.NetApi.Model.Meta.Storage.Hasher")), + 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 = + result = new ExpressionSyntax[] { SyntaxFactory.ObjectCreationExpression( SyntaxFactory.IdentifierName("System.Tuple"), @@ -577,7 +582,7 @@ private static ExpressionSyntax[] GetStorageMapStringRoslyn(string keyType, stri })), null), SyntaxFactory.ObjectCreationExpression( - SyntaxFactory.IdentifierName("System.Tuple"), + SyntaxFactory.IdentifierName("System.Tuple"), SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList( new ArgumentSyntax[] { From 75c7887091b6dae1d0a3ff8e06b0961f9d81e752 Mon Sep 17 00:00:00 2001 From: "cedric.decoster" Date: Mon, 29 May 2023 23:43:36 +0200 Subject: [PATCH 06/14] added roslyn generation --- .../Service/Generators/Base/SolutionGeneratorBase.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tools/Substrate.DotNet/Service/Generators/Base/SolutionGeneratorBase.cs b/Tools/Substrate.DotNet/Service/Generators/Base/SolutionGeneratorBase.cs index 83ea5b0..41effa1 100644 --- a/Tools/Substrate.DotNet/Service/Generators/Base/SolutionGeneratorBase.cs +++ b/Tools/Substrate.DotNet/Service/Generators/Base/SolutionGeneratorBase.cs @@ -58,7 +58,7 @@ protected NodeTypeResolver GenerateTypes(Dictionary nodeTypes, s case TypeDefEnum.Composite: { var type = nodeType as NodeTypeComposite; - StructBuilder.Init(ProjectName, type.Id, type, resolver) + StructBuilderRoslyn.Init(ProjectName, type.Id, type, resolver) .Create() .Build(write: write, out bool success, basePath); @@ -79,7 +79,7 @@ protected NodeTypeResolver GenerateTypes(Dictionary nodeTypes, s case TypeDefEnum.Array: { var type = nodeType as NodeTypeArray; - ArrayBuilder.Create(ProjectName, type.Id, type, resolver) + ArrayBuilderRoslyn.Create(ProjectName, type.Id, type, resolver) .Create() .Build(write: write, out bool success, basePath); From 91a144b6d4b4acd3c08e2a2bc184c2ab6b3be06d Mon Sep 17 00:00:00 2001 From: "cedric.decoster" Date: Tue, 30 May 2023 09:31:17 +0200 Subject: [PATCH 07/14] removed the duplicated check for now --- .../Service/Node/NodeTypeResolver.cs | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/Tools/Substrate.DotNet/Service/Node/NodeTypeResolver.cs b/Tools/Substrate.DotNet/Service/Node/NodeTypeResolver.cs index 77e7a90..e97f110 100644 --- a/Tools/Substrate.DotNet/Service/Node/NodeTypeResolver.cs +++ b/Tools/Substrate.DotNet/Service/Node/NodeTypeResolver.cs @@ -166,22 +166,23 @@ private Dictionary Resolve(Dictionary ty 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; - } - } - } + //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; } From 71d9ba1b126eefccb8ca70452a80c336410b0263 Mon Sep 17 00:00:00 2001 From: "cedric.decoster" Date: Tue, 30 May 2023 12:20:18 +0200 Subject: [PATCH 08/14] migrated CLientBuilder removed unused storageKey dict added proper properties --- .../Generators/Base/SolutionGeneratorBase.cs | 6 +- .../Service/Generators/NetApiGenerator.cs | 4 +- .../Service/Node/Base/BuilderBaseRoslyn.cs | 3 - .../Node/Base/ClientBuilderBaseRoslyn.cs | 21 +++ .../Service/Node/ClientBuilderRoslyn.cs | 121 ++++++++++++++++++ .../Service/Node/EventModuleBuilder.cs | 106 --------------- 6 files changed, 147 insertions(+), 114 deletions(-) create mode 100644 Tools/Substrate.DotNet/Service/Node/Base/ClientBuilderBaseRoslyn.cs create mode 100644 Tools/Substrate.DotNet/Service/Node/ClientBuilderRoslyn.cs delete mode 100644 Tools/Substrate.DotNet/Service/Node/EventModuleBuilder.cs diff --git a/Tools/Substrate.DotNet/Service/Generators/Base/SolutionGeneratorBase.cs b/Tools/Substrate.DotNet/Service/Generators/Base/SolutionGeneratorBase.cs index 41effa1..acb5f7d 100644 --- a/Tools/Substrate.DotNet/Service/Generators/Base/SolutionGeneratorBase.cs +++ b/Tools/Substrate.DotNet/Service/Generators/Base/SolutionGeneratorBase.cs @@ -58,7 +58,7 @@ protected NodeTypeResolver GenerateTypes(Dictionary nodeTypes, s case TypeDefEnum.Composite: { var type = nodeType as NodeTypeComposite; - StructBuilderRoslyn.Init(ProjectName, type.Id, type, resolver) + StructBuilder.Init(ProjectName, type.Id, type, resolver) .Create() .Build(write: write, out bool success, basePath); @@ -79,7 +79,7 @@ protected NodeTypeResolver GenerateTypes(Dictionary nodeTypes, s case TypeDefEnum.Array: { var type = nodeType as NodeTypeArray; - ArrayBuilderRoslyn.Create(ProjectName, type.Id, type, resolver) + ArrayBuilder.Create(ProjectName, type.Id, type, resolver) .Create() .Build(write: write, out bool success, basePath); @@ -103,7 +103,7 @@ private void CallVariant(string variantType, NodeTypeVariant nodeType, ref NodeT { case "Enum": { - EnumBuilderRoslyn.Init(ProjectName, nodeType.Id, nodeType, typeDict).Create().Build(write: write, out bool success, basePath); + EnumBuilder.Init(ProjectName, nodeType.Id, nodeType, typeDict).Create().Build(write: write, out bool success, basePath); if (!success) { Logger.Error($"Could not build type {nodeType.Id}!"); diff --git a/Tools/Substrate.DotNet/Service/Generators/NetApiGenerator.cs b/Tools/Substrate.DotNet/Service/Generators/NetApiGenerator.cs index bfd150d..42981e9 100644 --- a/Tools/Substrate.DotNet/Service/Generators/NetApiGenerator.cs +++ b/Tools/Substrate.DotNet/Service/Generators/NetApiGenerator.cs @@ -40,7 +40,7 @@ private static void GenerateModules(string projectName, Dictionary modulesResolved = new(); foreach (PalletModule module in modules.Values) { - ModuleGenBuilderRoslyn + ModuleGenBuilder .Init(projectName, module.Index, module, typeDict, nodeTypes) .Create() .Build(write: true, out bool _, basePath); @@ -48,7 +48,7 @@ private static void GenerateModules(string projectName, Dictionary ModuleNames { get; } + + protected ClientBuilderBaseRoslyn(string projectName, uint id, List moduleNames, NodeTypeResolver typeDict) + : base(projectName, id, typeDict) + { + ModuleNames = moduleNames; + + TargetUnit = TargetUnit.AddUsings( + SyntaxFactory.UsingDirective(SyntaxFactory.ParseName($"{ProjectName}.Generated.Storage"))); + } + } +} \ No newline at end of file diff --git a/Tools/Substrate.DotNet/Service/Node/ClientBuilderRoslyn.cs b/Tools/Substrate.DotNet/Service/Node/ClientBuilderRoslyn.cs new file mode 100644 index 0000000..d7c2a8d --- /dev/null +++ b/Tools/Substrate.DotNet/Service/Node/ClientBuilderRoslyn.cs @@ -0,0 +1,121 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Substrate.DotNet.Service.Node.Base; +using System.Collections.Generic; + +namespace Substrate.DotNet.Service.Node +{ + public class ClientBuilderRoslyn : ClientBuilderBaseRoslyn + { + private ClientBuilderRoslyn(string projectName, uint id, List moduleNames, NodeTypeResolver typeDict) : + base(projectName, id, moduleNames, typeDict) + { + } + + public static ClientBuilderRoslyn Init(string projectName, uint id, List moduleNames, NodeTypeResolver typeDict) + { + return new ClientBuilderRoslyn(projectName, id, moduleNames, typeDict); + } + + public override ClientBuilderRoslyn Create() + { + ClassName = "SubstrateClientExt"; + NamespaceName = $"{ProjectName}.Generated"; + + 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) + { + + // 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/EventModuleBuilder.cs b/Tools/Substrate.DotNet/Service/Node/EventModuleBuilder.cs deleted file mode 100644 index 2d41ec8..0000000 --- a/Tools/Substrate.DotNet/Service/Node/EventModuleBuilder.cs +++ /dev/null @@ -1,106 +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)); - } - } -} \ No newline at end of file From 9a52839ede770dc6a18dbfdf68e890bda1fbb5c3 Mon Sep 17 00:00:00 2001 From: "cedric.decoster" Date: Tue, 30 May 2023 17:27:49 +0200 Subject: [PATCH 09/14] wip --- .../Service/Generators/NetApiGenerator.cs | 2 +- .../Generators/RestServiceGenerator.cs | 2 +- .../Service/Node/Base/ModulesBuilderBase.cs | 20 -- ...estServiceControllerModuleBuilderRoslyn.cs | 245 +++++++++++++ .../RestServiceStorageModuleBuilderRoslyn.cs | 323 ++++++++++++++++++ 5 files changed, 570 insertions(+), 22 deletions(-) delete mode 100644 Tools/Substrate.DotNet/Service/Node/Base/ModulesBuilderBase.cs create mode 100644 Tools/Substrate.DotNet/Service/Node/RestServiceControllerModuleBuilderRoslyn.cs create mode 100644 Tools/Substrate.DotNet/Service/Node/RestServiceStorageModuleBuilderRoslyn.cs diff --git a/Tools/Substrate.DotNet/Service/Generators/NetApiGenerator.cs b/Tools/Substrate.DotNet/Service/Generators/NetApiGenerator.cs index 42981e9..c13068a 100644 --- a/Tools/Substrate.DotNet/Service/Generators/NetApiGenerator.cs +++ b/Tools/Substrate.DotNet/Service/Generators/NetApiGenerator.cs @@ -48,7 +48,7 @@ private static void GenerateModules(string projectName, 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/RestServiceControllerModuleBuilderRoslyn.cs b/Tools/Substrate.DotNet/Service/Node/RestServiceControllerModuleBuilderRoslyn.cs new file mode 100644 index 0000000..86e21f5 --- /dev/null +++ b/Tools/Substrate.DotNet/Service/Node/RestServiceControllerModuleBuilderRoslyn.cs @@ -0,0 +1,245 @@ +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 +{ + public class RestServiceControllerModuleBuilderRoslyn : ModuleBuilderBaseRoslyn + { + private string NetApiProjectName { get; } + + private RestServiceControllerModuleBuilderRoslyn(string projectName, string netApiProjectName, uint id, PalletModule module, NodeTypeResolver typeDict, Dictionary nodeTypes) : + base(projectName, id, module, typeDict, nodeTypes) + { + NetApiProjectName = netApiProjectName; + } + + public static RestServiceControllerModuleBuilderRoslyn Init(string projectName, string netApiProjectName, uint id, PalletModule module, NodeTypeResolver typeDict, Dictionary nodeTypes) + { + return new RestServiceControllerModuleBuilderRoslyn(projectName, netApiProjectName, id, module, typeDict, nodeTypes); + } + + public override RestServiceControllerModuleBuilderRoslyn Create() + { + if (Module.Storage == null) + { + Success = false; + return this; + } + + FileName = Module.Storage.Prefix + "Controller"; + ReferenzName = $"{ProjectName}.Generated.Controller.{FileName}"; + NamespaceName = $"{ProjectName}.Generated.Controller"; + + 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); + + TargetUnit = TargetUnit.AddUsings(usingDirectives.ToArray()); + + return this; + } + + private void CreateController(NamespaceDeclarationSyntax namespaceDeclaration) + { + ClassName = FileName; + + // 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) + { + 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 = targetClass.AddMembers(methodDeclaration); + } + } + + namespaceDeclaration = namespaceDeclaration.AddMembers(targetClass); + + TargetUnit = TargetUnit.AddMembers(namespaceDeclaration); + } + + 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) + { + 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"))); + + invokeExpression = SyntaxFactory.IdentifierName("key"); + } + else + { + throw new NotImplementedException(); + } + + string indentifier = $"Get{entry.Name}"; + + 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))))))))))); + + + + // Add parameters to method + foreach (ParameterSyntax parameter in methodParameters) + { + 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/RestServiceStorageModuleBuilderRoslyn.cs b/Tools/Substrate.DotNet/Service/Node/RestServiceStorageModuleBuilderRoslyn.cs new file mode 100644 index 0000000..85ffd95 --- /dev/null +++ b/Tools/Substrate.DotNet/Service/Node/RestServiceStorageModuleBuilderRoslyn.cs @@ -0,0 +1,323 @@ +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; +using System; +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 +{ + public class RestServiceStorageModuleBuilderRoslyn : ModuleBuilderBaseRoslyn + { + private RestServiceStorageModuleBuilderRoslyn(string projectName, uint id, PalletModule module, NodeTypeResolver typeDict, Dictionary nodeTypes) : + base(projectName, id, module, typeDict, nodeTypes) + { + } + + public static RestServiceStorageModuleBuilderRoslyn Init(string projectName, uint id, PalletModule module, NodeTypeResolver typeDict, Dictionary nodeTypes) + { + return new RestServiceStorageModuleBuilderRoslyn(projectName, id, module, typeDict, nodeTypes); + } + + public override RestServiceStorageModuleBuilderRoslyn Create() + { + if (Module.Storage == null) + { + Success = false; + return this; + } + + FileName = Module.Storage.Prefix + "Storage"; + ReferenzName = $"{ProjectName}.Generated.Storage.{FileName}"; + NamespaceName = $"{ProjectName}.Generated.Storage"; + + 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)); + + return this; + } + + private NamespaceDeclarationSyntax CreateStorage(NamespaceDeclarationSyntax namespaceDeclaration) + { + // Setting ClassName + ClassName = Module.Storage.Prefix + "Storage"; + + // Creating the interface declaration + InterfaceDeclarationSyntax targetInterface = SyntaxFactory + .InterfaceDeclaration($"I{ClassName}") + .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) + .AddBaseListTypes(SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName("IStorage"))) + .WithLeadingTrivia(SyntaxFactory.Comment($"// 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(SyntaxFactory.Comment($"// {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" })); + + if (Module.Storage.Entries != null) + { + ParameterSyntax keyParamter = SyntaxFactory.Parameter(SyntaxFactory.Identifier("key")) + .WithType(SyntaxFactory.ParseTypeName("string")); + + ParameterSyntax dataParamter = SyntaxFactory.Parameter(SyntaxFactory.Identifier("data")) + .WithType(SyntaxFactory.ParseTypeName("string")); + + var constructorStatements = new List(); + var initializeStatements = new List(); + + //var fields = new List(); + var props = new List(); + + var onUpdates = new List(); + var getStorages = new List(); + + foreach (Entry entry in Module.Storage.Entries) + { + TypeSyntax baseReturnType; + TypeSyntax returnType; + ArgumentListSyntax updateExpression, tryGetExpression; + + if (entry.StorageType == Storage.Type.Plain) + { + NodeTypeResolved fullItem = GetFullItemPath(entry.TypeMap.Item1); + baseReturnType = SyntaxFactory.ParseTypeName(fullItem.ToString()); + returnType = SyntaxFactory.ParseTypeName($"TypedStorage<{fullItem.ToString()}>"); + + updateExpression = SyntaxFactory.ArgumentList().AddArguments( + SyntaxFactory.Argument(SyntaxFactory.IdentifierName(dataParamter.Identifier))); + + tryGetExpression = SyntaxFactory.ArgumentList(); + } + 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); + baseReturnType = SyntaxFactory.ParseTypeName(value.ToString()); + returnType = SyntaxFactory.ParseTypeName($"TypedMapStorage<{value.ToString()}>"); + + 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.IdentifierName("result")) + .WithRefOrOutKeyword(SyntaxFactory.Token(SyntaxKind.OutKeyword))); + } + else + { + throw new NotImplementedException(); + } + + string fieldName = $"{entry.Name.MakePrivateField()}TypedStorage"; + + // 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 + 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 + 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 + 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 + 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}" })); + + + // create get and gets + MethodDeclarationSyntax getInterfaceMethod = SyntaxFactory + .MethodDeclaration(baseReturnType, $"Get{entry.Name}") + .WithLeadingTrivia(GetCommentsRoslyn(entry.Docs)) + .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)); + + targetInterface = targetInterface.AddMembers(getInterfaceMethod); + + + MethodDeclarationSyntax getStorageMethod = SyntaxFactory + .MethodDeclaration(baseReturnType, $"Get{entry.Name}") + .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) + .WithLeadingTrivia(GetCommentsRoslyn(entry.Docs)); + + if (tryGetExpression.Arguments.Count == 0) + { + 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 = onUpdateMethod + .AddParameterListParameters(keyParamter, dataParamter); + + getStorageMethod = getStorageMethod.AddParameterListParameters( + SyntaxFactory.Parameter(dataParamter.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)))))); + + } + + onUpdates.Add(onUpdateMethod); + getStorages.Add(getStorageMethod); + } + + //targetClass = targetClass.AddMembers(fields.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(onUpdates.ToArray()); + + targetClass = targetClass.AddMembers(getStorages.ToArray()); + + namespaceDeclaration = namespaceDeclaration.AddMembers(targetInterface); + + namespaceDeclaration = namespaceDeclaration.AddMembers(targetClass); + + } + + return namespaceDeclaration; + } + + } +} \ No newline at end of file From eadc3e009775cb8d0535a741d027c9ce35a0e9f7 Mon Sep 17 00:00:00 2001 From: "cedric.decoster" Date: Tue, 30 May 2023 18:03:21 +0200 Subject: [PATCH 10/14] fixing more on the rest client --- .../RestServiceStorageModuleBuilderRoslyn.cs | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/Tools/Substrate.DotNet/Service/Node/RestServiceStorageModuleBuilderRoslyn.cs b/Tools/Substrate.DotNet/Service/Node/RestServiceStorageModuleBuilderRoslyn.cs index 85ffd95..77d7930 100644 --- a/Tools/Substrate.DotNet/Service/Node/RestServiceStorageModuleBuilderRoslyn.cs +++ b/Tools/Substrate.DotNet/Service/Node/RestServiceStorageModuleBuilderRoslyn.cs @@ -63,13 +63,13 @@ private NamespaceDeclarationSyntax CreateStorage(NamespaceDeclarationSyntax name .InterfaceDeclaration($"I{ClassName}") .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) .AddBaseListTypes(SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName("IStorage"))) - .WithLeadingTrivia(SyntaxFactory.Comment($"// I{ClassName} interface definition")); + .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(SyntaxFactory.Comment($"// {ClassName} class definition")); + .WithLeadingTrivia(GetCommentsRoslyn(new string[] { $"{ClassName} class definition" })); // Creating the constructor ConstructorDeclarationSyntax constructor = SyntaxFactory.ConstructorDeclaration(ClassName) @@ -106,6 +106,7 @@ private NamespaceDeclarationSyntax CreateStorage(NamespaceDeclarationSyntax name //var fields = new List(); var props = new List(); + var interfaceMethods = new List(); var onUpdates = new List(); var getStorages = new List(); @@ -119,7 +120,7 @@ private NamespaceDeclarationSyntax CreateStorage(NamespaceDeclarationSyntax name { NodeTypeResolved fullItem = GetFullItemPath(entry.TypeMap.Item1); baseReturnType = SyntaxFactory.ParseTypeName(fullItem.ToString()); - returnType = SyntaxFactory.ParseTypeName($"TypedStorage<{fullItem.ToString()}>"); + returnType = SyntaxFactory.ParseTypeName($"TypedStorage<{fullItem}>"); updateExpression = SyntaxFactory.ArgumentList().AddArguments( SyntaxFactory.Argument(SyntaxFactory.IdentifierName(dataParamter.Identifier))); @@ -133,7 +134,7 @@ private NamespaceDeclarationSyntax CreateStorage(NamespaceDeclarationSyntax name NodeTypeResolved key = GetFullItemPath(typeMap.Key); NodeTypeResolved value = GetFullItemPath(typeMap.Value); baseReturnType = SyntaxFactory.ParseTypeName(value.ToString()); - returnType = SyntaxFactory.ParseTypeName($"TypedMapStorage<{value.ToString()}>"); + returnType = SyntaxFactory.ParseTypeName($"TypedMapStorage<{value}>"); updateExpression = SyntaxFactory.ArgumentList().AddArguments( SyntaxFactory.Argument(SyntaxFactory.IdentifierName(keyParamter.Identifier)), @@ -232,12 +233,9 @@ private NamespaceDeclarationSyntax CreateStorage(NamespaceDeclarationSyntax name // create get and gets MethodDeclarationSyntax getInterfaceMethod = SyntaxFactory .MethodDeclaration(baseReturnType, $"Get{entry.Name}") - .WithLeadingTrivia(GetCommentsRoslyn(entry.Docs)) + .WithLeadingTrivia(GetCommentsRoslyn(entry.Docs, null, entry.Name)) .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)); - targetInterface = targetInterface.AddMembers(getInterfaceMethod); - - MethodDeclarationSyntax getStorageMethod = SyntaxFactory .MethodDeclaration(baseReturnType, $"Get{entry.Name}") .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) @@ -262,10 +260,14 @@ private NamespaceDeclarationSyntax CreateStorage(NamespaceDeclarationSyntax name onUpdateMethod = onUpdateMethod .AddParameterListParameters(keyParamter, dataParamter); - getStorageMethod = getStorageMethod.AddParameterListParameters( - SyntaxFactory.Parameter(dataParamter.Identifier) + getInterfaceMethod = getInterfaceMethod.AddParameterListParameters( + SyntaxFactory.Parameter(keyParamter.Identifier) .WithType(keyParamter.Type)); + getStorageMethod = getStorageMethod.AddParameterListParameters( + SyntaxFactory.Parameter(dataParamter.Identifier) + .WithType(keyParamter.Type)); + getStorageMethod = getStorageMethod.WithBody( SyntaxFactory.Block( SyntaxFactory.IfStatement( @@ -292,11 +294,13 @@ private NamespaceDeclarationSyntax CreateStorage(NamespaceDeclarationSyntax name } + interfaceMethods.Add(getInterfaceMethod); onUpdates.Add(onUpdateMethod); getStorages.Add(getStorageMethod); } //targetClass = targetClass.AddMembers(fields.ToArray()); + targetInterface = targetInterface.AddMembers(interfaceMethods.ToArray()); constructor = constructor.WithBody(SyntaxFactory.Block(constructorStatements)); targetClass = targetClass.AddMembers(constructor); From 66aadf1959b727848a3699b776d38cacde6212fe Mon Sep 17 00:00:00 2001 From: "cedric.decoster" Date: Tue, 30 May 2023 21:57:31 +0200 Subject: [PATCH 11/14] initial draft of migration to roslyn --- .../Generators/Base/SolutionGeneratorBase.cs | 14 ++++----- .../Service/Generators/NetApiGenerator.cs | 4 +-- .../Generators/RestServiceGenerator.cs | 2 +- .../Service/Node/ClientBuilderRoslyn.cs | 30 +++++++++---------- .../Service/Node/ModuleGenBuilderRoslyn.cs | 5 ++-- .../RestServiceStorageModuleBuilderRoslyn.cs | 25 +++++++++------- 6 files changed, 42 insertions(+), 38 deletions(-) diff --git a/Tools/Substrate.DotNet/Service/Generators/Base/SolutionGeneratorBase.cs b/Tools/Substrate.DotNet/Service/Generators/Base/SolutionGeneratorBase.cs index acb5f7d..bf39257 100644 --- a/Tools/Substrate.DotNet/Service/Generators/Base/SolutionGeneratorBase.cs +++ b/Tools/Substrate.DotNet/Service/Generators/Base/SolutionGeneratorBase.cs @@ -58,7 +58,7 @@ protected NodeTypeResolver GenerateTypes(Dictionary nodeTypes, s case TypeDefEnum.Composite: { var type = nodeType as NodeTypeComposite; - StructBuilder.Init(ProjectName, type.Id, type, resolver) + StructBuilderRoslyn.Init(ProjectName, type.Id, type, resolver) .Create() .Build(write: write, out bool success, basePath); @@ -79,7 +79,7 @@ protected NodeTypeResolver GenerateTypes(Dictionary nodeTypes, s case TypeDefEnum.Array: { var type = nodeType as NodeTypeArray; - ArrayBuilder.Create(ProjectName, type.Id, type, resolver) + ArrayBuilderRoslyn.Create(ProjectName, type.Id, type, resolver) .Create() .Build(write: write, out bool success, basePath); @@ -103,7 +103,7 @@ private void CallVariant(string variantType, NodeTypeVariant nodeType, ref NodeT { case "Enum": { - EnumBuilder.Init(ProjectName, nodeType.Id, nodeType, typeDict).Create().Build(write: write, out bool success, basePath); + EnumBuilderRoslyn.Init(ProjectName, nodeType.Id, nodeType, typeDict).Create().Build(write: write, out bool success, basePath); if (!success) { Logger.Error($"Could not build type {nodeType.Id}!"); @@ -127,10 +127,10 @@ private void CallVariant(string variantType, NodeTypeVariant nodeType, ref NodeT 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(); @@ -195,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/Generators/NetApiGenerator.cs b/Tools/Substrate.DotNet/Service/Generators/NetApiGenerator.cs index c13068a..7a46b8e 100644 --- a/Tools/Substrate.DotNet/Service/Generators/NetApiGenerator.cs +++ b/Tools/Substrate.DotNet/Service/Generators/NetApiGenerator.cs @@ -40,7 +40,7 @@ private static void GenerateModules(string projectName, Dictionary modulesResolved = new(); foreach (PalletModule module in modules.Values) { - ModuleGenBuilder + ModuleGenBuilderRoslyn .Init(projectName, module.Index, module, typeDict, nodeTypes) .Create() .Build(write: true, out bool _, basePath); @@ -48,7 +48,7 @@ private static void GenerateModules(string projectName, 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); + // 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())))); + 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) diff --git a/Tools/Substrate.DotNet/Service/Node/ModuleGenBuilderRoslyn.cs b/Tools/Substrate.DotNet/Service/Node/ModuleGenBuilderRoslyn.cs index b1ef41b..80fab2e 100644 --- a/Tools/Substrate.DotNet/Service/Node/ModuleGenBuilderRoslyn.cs +++ b/Tools/Substrate.DotNet/Service/Node/ModuleGenBuilderRoslyn.cs @@ -78,7 +78,7 @@ private NamespaceDeclarationSyntax CreateStorage(NamespaceDeclarationSyntax type SyntaxFactory.SingletonSeparatedList( SyntaxFactory.VariableDeclarator(SyntaxFactory.Identifier("_client"))))) .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PrivateKeyword))) - .WithLeadingTrivia(SyntaxFactory.Comment("// Substrate client for the storage calls.")); + .WithLeadingTrivia(GetCommentsRoslyn(new string[] { "Substrate client for the storage calls." })); targetClass = targetClass.AddMembers(clientField); // Add parameters. @@ -123,7 +123,8 @@ private NamespaceDeclarationSyntax CreateStorage(NamespaceDeclarationSyntax type methodInvoke = SyntaxFactory.ExpressionStatement(SyntaxFactory.InvocationExpression(SyntaxFactory.IdentifierName(parameterMethod.Identifier))); // add storage key mapping in constructor - constructor = constructor.AddBodyStatements(AddPropertyValuesRoslyn(GetStorageMapStringRoslyn("", returnValueStr.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) { diff --git a/Tools/Substrate.DotNet/Service/Node/RestServiceStorageModuleBuilderRoslyn.cs b/Tools/Substrate.DotNet/Service/Node/RestServiceStorageModuleBuilderRoslyn.cs index 77d7930..9afc154 100644 --- a/Tools/Substrate.DotNet/Service/Node/RestServiceStorageModuleBuilderRoslyn.cs +++ b/Tools/Substrate.DotNet/Service/Node/RestServiceStorageModuleBuilderRoslyn.cs @@ -107,8 +107,7 @@ private NamespaceDeclarationSyntax CreateStorage(NamespaceDeclarationSyntax name var props = new List(); var interfaceMethods = new List(); - var onUpdates = new List(); - var getStorages = new List(); + var methodOnAndGet = new List(); foreach (Entry entry in Module.Storage.Entries) { @@ -142,8 +141,13 @@ private NamespaceDeclarationSyntax CreateStorage(NamespaceDeclarationSyntax name tryGetExpression = SyntaxFactory.ArgumentList().AddArguments( SyntaxFactory.Argument(SyntaxFactory.IdentifierName(keyParamter.Identifier)), - SyntaxFactory.Argument(SyntaxFactory.IdentifierName("result")) - .WithRefOrOutKeyword(SyntaxFactory.Token(SyntaxKind.OutKeyword))); + SyntaxFactory.Argument( + SyntaxFactory.DeclarationExpression( + baseReturnType, + SyntaxFactory.SingleVariableDesignation(SyntaxFactory.Identifier("result")))) + .WithRefOrOutKeyword(SyntaxFactory.Token(SyntaxKind.OutKeyword)) + ); + } else { @@ -239,7 +243,7 @@ private NamespaceDeclarationSyntax CreateStorage(NamespaceDeclarationSyntax name MethodDeclarationSyntax getStorageMethod = SyntaxFactory .MethodDeclaration(baseReturnType, $"Get{entry.Name}") .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) - .WithLeadingTrivia(GetCommentsRoslyn(entry.Docs)); + .WithLeadingTrivia(GetCommentsRoslyn(entry.Docs, null, entry.Name)); if (tryGetExpression.Arguments.Count == 0) { @@ -265,7 +269,7 @@ private NamespaceDeclarationSyntax CreateStorage(NamespaceDeclarationSyntax name .WithType(keyParamter.Type)); getStorageMethod = getStorageMethod.AddParameterListParameters( - SyntaxFactory.Parameter(dataParamter.Identifier) + SyntaxFactory.Parameter(keyParamter.Identifier) .WithType(keyParamter.Type)); getStorageMethod = getStorageMethod.WithBody( @@ -295,8 +299,9 @@ private NamespaceDeclarationSyntax CreateStorage(NamespaceDeclarationSyntax name } interfaceMethods.Add(getInterfaceMethod); - onUpdates.Add(onUpdateMethod); - getStorages.Add(getStorageMethod); + + methodOnAndGet.Add(onUpdateMethod); + methodOnAndGet.Add(getStorageMethod); } //targetClass = targetClass.AddMembers(fields.ToArray()); @@ -310,9 +315,7 @@ private NamespaceDeclarationSyntax CreateStorage(NamespaceDeclarationSyntax name initializeAsyncMethod = initializeAsyncMethod.WithBody(SyntaxFactory.Block(initializeStatements)); targetClass = targetClass.AddMembers(initializeAsyncMethod); - targetClass = targetClass.AddMembers(onUpdates.ToArray()); - - targetClass = targetClass.AddMembers(getStorages.ToArray()); + targetClass = targetClass.AddMembers(methodOnAndGet.ToArray()); namespaceDeclaration = namespaceDeclaration.AddMembers(targetInterface); From 23564a31168fd2ac16f0e121eaabe6ff6a6da68b Mon Sep 17 00:00:00 2001 From: "cedric.decoster" Date: Tue, 30 May 2023 22:03:54 +0200 Subject: [PATCH 12/14] archived old codedom --- .../Generators/Base/SolutionGeneratorBase.cs | 6 +- .../Service/Generators/NetApiGenerator.cs | 4 +- .../Generators/RestServiceGenerator.cs | 4 +- .../Service/Node/ArrayBuilder.cs | 228 +++--- .../Service/Node/ArrayBuilderRoslyn.cs | 162 ---- .../Service/Node/Base/BuilderBase.cs | 195 ++--- .../Service/Node/Base/BuilderBaseRoslyn.cs | 197 ----- .../Service/Node/Base/ClientBuilderBase.cs | 11 +- .../Node/Base/ClientBuilderBaseRoslyn.cs | 21 - .../Service/Node/Base/ModuleBuilderBase.cs | 2 +- .../Service/Node/ClientBuilder.cs | 167 +++-- .../Service/Node/ClientBuilderRoslyn.cs | 121 --- .../Service/Node/EnumBuilder.cs | 259 ++----- .../Service/Node/EnumBuilderRoslyn.cs | 127 ---- .../Service/Node/ModuleGenBuilder.cs | 707 ++++++++++-------- .../Service/Node/ModuleGenBuilderRoslyn.cs | 612 --------------- .../Service/Node/Old/ArrayBuilderCodeDom.cs | 184 +++++ .../Service/Node/Old/BuilderBaseCodeDom.cs | 180 +++++ .../Node/Old/ClientBuilderBaseCodeDom.cs | 18 + .../Service/Node/Old/ClientBuilderCodeDom.cs | 114 +++ .../Service/Node/Old/EnumBuilderCodeDom.cs | 280 +++++++ .../ModuleBuilderBaseCodeDom.cs} | 4 +- .../Node/Old/ModuleGenBuilderCodeDom.cs | 535 +++++++++++++ ...stServiceControllerModuleBuilderCodeDom.cs | 195 +++++ .../RestServiceStorageModuleBuilderCodeDom.cs | 253 +++++++ .../Service/Node/Old/StructBuilderCodeDom.cs | 198 +++++ .../TypeBuilderBaseCodeDom.cs} | 4 +- .../RestServiceControllerModuleBuilder.cs | 326 ++++---- ...estServiceControllerModuleBuilderRoslyn.cs | 245 ------ .../Node/RestServiceStorageModuleBuilder.cs | 401 ++++++---- .../RestServiceStorageModuleBuilderRoslyn.cs | 330 -------- .../Service/Node/StructBuilder.cs | 192 ++--- .../Service/Node/StructBuilderRoslyn.cs | 216 ------ 33 files changed, 3249 insertions(+), 3249 deletions(-) delete mode 100644 Tools/Substrate.DotNet/Service/Node/ArrayBuilderRoslyn.cs delete mode 100644 Tools/Substrate.DotNet/Service/Node/Base/BuilderBaseRoslyn.cs delete mode 100644 Tools/Substrate.DotNet/Service/Node/Base/ClientBuilderBaseRoslyn.cs delete mode 100644 Tools/Substrate.DotNet/Service/Node/ClientBuilderRoslyn.cs delete mode 100644 Tools/Substrate.DotNet/Service/Node/EnumBuilderRoslyn.cs delete mode 100644 Tools/Substrate.DotNet/Service/Node/ModuleGenBuilderRoslyn.cs create mode 100644 Tools/Substrate.DotNet/Service/Node/Old/ArrayBuilderCodeDom.cs create mode 100644 Tools/Substrate.DotNet/Service/Node/Old/BuilderBaseCodeDom.cs create mode 100644 Tools/Substrate.DotNet/Service/Node/Old/ClientBuilderBaseCodeDom.cs create mode 100644 Tools/Substrate.DotNet/Service/Node/Old/ClientBuilderCodeDom.cs create mode 100644 Tools/Substrate.DotNet/Service/Node/Old/EnumBuilderCodeDom.cs rename Tools/Substrate.DotNet/Service/Node/{Base/ModuleBuilderBaseRoslyn.cs => Old/ModuleBuilderBaseCodeDom.cs} (78%) create mode 100644 Tools/Substrate.DotNet/Service/Node/Old/ModuleGenBuilderCodeDom.cs create mode 100644 Tools/Substrate.DotNet/Service/Node/Old/RestServiceControllerModuleBuilderCodeDom.cs create mode 100644 Tools/Substrate.DotNet/Service/Node/Old/RestServiceStorageModuleBuilderCodeDom.cs create mode 100644 Tools/Substrate.DotNet/Service/Node/Old/StructBuilderCodeDom.cs rename Tools/Substrate.DotNet/Service/Node/{Base/TypeBuilderBaseRoslyn.cs => Old/TypeBuilderBaseCodeDom.cs} (60%) delete mode 100644 Tools/Substrate.DotNet/Service/Node/RestServiceControllerModuleBuilderRoslyn.cs delete mode 100644 Tools/Substrate.DotNet/Service/Node/RestServiceStorageModuleBuilderRoslyn.cs delete mode 100644 Tools/Substrate.DotNet/Service/Node/StructBuilderRoslyn.cs diff --git a/Tools/Substrate.DotNet/Service/Generators/Base/SolutionGeneratorBase.cs b/Tools/Substrate.DotNet/Service/Generators/Base/SolutionGeneratorBase.cs index bf39257..56935b1 100644 --- a/Tools/Substrate.DotNet/Service/Generators/Base/SolutionGeneratorBase.cs +++ b/Tools/Substrate.DotNet/Service/Generators/Base/SolutionGeneratorBase.cs @@ -58,7 +58,7 @@ protected NodeTypeResolver GenerateTypes(Dictionary nodeTypes, s case TypeDefEnum.Composite: { var type = nodeType as NodeTypeComposite; - StructBuilderRoslyn.Init(ProjectName, type.Id, type, resolver) + StructBuilder.Init(ProjectName, type.Id, type, resolver) .Create() .Build(write: write, out bool success, basePath); @@ -79,7 +79,7 @@ protected NodeTypeResolver GenerateTypes(Dictionary nodeTypes, s case TypeDefEnum.Array: { var type = nodeType as NodeTypeArray; - ArrayBuilderRoslyn.Create(ProjectName, type.Id, type, resolver) + ArrayBuilder.Create(ProjectName, type.Id, type, resolver) .Create() .Build(write: write, out bool success, basePath); @@ -103,7 +103,7 @@ private void CallVariant(string variantType, NodeTypeVariant nodeType, ref NodeT { case "Enum": { - EnumBuilderRoslyn.Init(ProjectName, nodeType.Id, nodeType, typeDict).Create().Build(write: write, out bool success, basePath); + EnumBuilder.Init(ProjectName, nodeType.Id, nodeType, typeDict).Create().Build(write: write, out bool success, basePath); if (!success) { Logger.Error($"Could not build type {nodeType.Id}!"); diff --git a/Tools/Substrate.DotNet/Service/Generators/NetApiGenerator.cs b/Tools/Substrate.DotNet/Service/Generators/NetApiGenerator.cs index 7a46b8e..c13068a 100644 --- a/Tools/Substrate.DotNet/Service/Generators/NetApiGenerator.cs +++ b/Tools/Substrate.DotNet/Service/Generators/NetApiGenerator.cs @@ -40,7 +40,7 @@ private static void GenerateModules(string projectName, Dictionary modulesResolved = new(); foreach (PalletModule module in modules.Values) { - ModuleGenBuilderRoslyn + ModuleGenBuilder .Init(projectName, module.Index, module, typeDict, nodeTypes) .Create() .Build(write: true, out bool _, basePath); @@ -48,7 +48,7 @@ private static void GenerateModules(string projectName, Dictionary()")); - 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,107 +83,79 @@ 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); + + 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")))))); - CodeMemberMethod encodeMethod = ArrayBuilder.GetEncode(); - targetClass.Members.Add(encodeMethod); + targetClass = targetClass.AddMembers(createMethod); - CodeMemberMethod decodeMethod = ArrayBuilder.GetDecode(fullItem.ToString()); - targetClass.Members.Add(decodeMethod); + NamespaceDeclarationSyntax namespaceDeclaration = SyntaxFactory + .NamespaceDeclaration(SyntaxFactory.ParseName(NamespaceName)) + .AddMembers(targetClass); + + 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; } } diff --git a/Tools/Substrate.DotNet/Service/Node/ArrayBuilderRoslyn.cs b/Tools/Substrate.DotNet/Service/Node/ArrayBuilderRoslyn.cs deleted file mode 100644 index aa38d83..0000000 --- a/Tools/Substrate.DotNet/Service/Node/ArrayBuilderRoslyn.cs +++ /dev/null @@ -1,162 +0,0 @@ -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.Linq; - -namespace Substrate.DotNet.Service.Node -{ - public class ArrayBuilderRoslyn : TypeBuilderBaseRoslyn - { - public static int Counter = 0; - - private ArrayBuilderRoslyn(string projectName, uint id, NodeTypeArray typeDef, NodeTypeResolver typeDict) - : base(projectName, id, typeDef, typeDict) - { - } - - private static MethodDeclarationSyntax GetDecodeRoslyn(string baseType) - { - 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 = 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 MethodDeclarationSyntax GetEncodeRoslyn() - { - 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; - } - - public static ArrayBuilderRoslyn Create(string projectName, uint id, NodeTypeArray nodeType, NodeTypeResolver typeDict) - { - return new ArrayBuilderRoslyn(projectName, id, nodeType, typeDict); - } - - public override TypeBuilderBaseRoslyn Create() - { - var typeDef = TypeDef as NodeTypeArray; - - NodeTypeResolved fullItem = GetFullItemPath(typeDef.TypeId); - - ClassName = $"Arr{typeDef.Length}{fullItem.ClassName}"; - - 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}"; - - 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); - - 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 = targetClass.WithLeadingTrivia(GetCommentsRoslyn(typeDef.Docs, typeDef)); - - targetClass = targetClass.AddMembers(GetEncodeRoslyn(), GetDecodeRoslyn(fullItem.ToString())); - - 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); - - NamespaceDeclarationSyntax namespaceDeclaration = SyntaxFactory - .NamespaceDeclaration(SyntaxFactory.ParseName(NamespaceName)) - .AddMembers(targetClass); - - CompilationUnitSyntax compilationUnit = SyntaxFactory.CompilationUnit() - .AddMembers(namespaceDeclaration); - - TargetUnit = TargetUnit.AddMembers(compilationUnit.Members.ToArray()); - - 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 649a6af..0b0c3bc 100644 --- a/Tools/Substrate.DotNet/Service/Node/Base/BuilderBase.cs +++ b/Tools/Substrate.DotNet/Service/Node/Base/BuilderBase.cs @@ -1,19 +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; } - private NodeTypeResolver Resolver { get; } + protected NodeTypeResolver Resolver { get; } public bool Success { get; set; } @@ -27,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)) + if (SyntaxFacts.GetKeywordKind(parameterName) != SyntaxKind.None) { - Success = false; - return null; + // If it is a keyword, create a verbatim identifier which adds '@' prefix + parameterName = "@" + parameterName; } - return fullItem; + return parameterName; } - public static CodeCommentStatementCollection GetComments(string[] docs, NodeType typeDef = null, - string typeName = null) - { - CodeCommentStatementCollection comments = new() - { - new CodeCommentStatement("", true) - }; - - if (typeDef != null) - { - 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 (docs != null) - { - foreach (string doc in docs) - { - comments.Add(new CodeCommentStatement(doc, true)); - } - } - - comments.Add(new CodeCommentStatement("", true)); - return comments; - } - - 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) @@ -119,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()); } } @@ -166,15 +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"))); + + AttributeArgumentSyntax attributeArgument = SyntaxFactory.AttributeArgument( + SyntaxFactory.ParseExpression($"TypeDefEnum.{typeDef.TypeDef}")); - targetClass.CustomAttributes.Add(new CodeAttributeDeclaration(new CodeTypeReference("SubstrateNodeType"), new CodeAttributeArgument( - new CodeSnippetExpression($"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/BuilderBaseRoslyn.cs b/Tools/Substrate.DotNet/Service/Node/Base/BuilderBaseRoslyn.cs deleted file mode 100644 index 6780229..0000000 --- a/Tools/Substrate.DotNet/Service/Node/Base/BuilderBaseRoslyn.cs +++ /dev/null @@ -1,197 +0,0 @@ -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 BuilderBaseRoslyn - { - protected static readonly List Files = new(); - - public uint Id { get; } - - protected NodeTypeResolver Resolver { get; } - - public bool Success { get; set; } - - public string NamespaceName { get; protected set; } - - internal string FileName { get; set; } - - public string ClassName { get; set; } - - public string ReferenzName { get; set; } - - public string ProjectName { get; private set; } - - public CompilationUnitSyntax TargetUnit { get; set; } - - public abstract BuilderBaseRoslyn Create(); - - protected BuilderBaseRoslyn(string projectName, uint id, NodeTypeResolver resolver) - { - ProjectName = projectName; - Id = id; - Resolver = resolver; - TargetUnit = SyntaxFactory.CompilationUnit() - .AddUsings( - SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("Substrate.NetApi.Model.Types.Base")), - SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("System.Collections.Generic"))); - Success = true; - } - - public static string EscapeIfKeyword(string parameterName) - { - if (SyntaxFacts.GetKeywordKind(parameterName) != SyntaxKind.None) - { - // If it is a keyword, create a verbatim identifier which adds '@' prefix - parameterName = "@" + parameterName; - } - - return parameterName; - } - - public NodeTypeResolved GetFullItemPath(uint typeId) - { - if (!Resolver.TypeNames.TryGetValue(typeId, out NodeTypeResolved fullItem)) - { - Success = false; - return null; - } - - return fullItem; - } - - public virtual void Build(bool write, out bool success, string basePath = null) - { - success = Success; - if (write && Success) - { - string path = GetPath(basePath); - - Directory.CreateDirectory(Path.GetDirectoryName(path)); - - if (!Files.Contains(path)) - { - Files.Add(path); - } - else - { - Log.Warning($"Overwriting[BUG]: {path}"); - } - - // add autogenerated header - TargetUnit = TargetUnit.WithLeadingTrivia(SyntaxFactory.TriviaList(HeaderComment)); - - using StreamWriter sourceWriter = new(path); - sourceWriter.Write(TargetUnit.NormalizeWhitespace().ToFullString()); - } - } - - private string GetPath(string basePath) - { - var space = NamespaceName.Split('.').ToList(); - - 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. - space = space.TakeLast(space.Count - 2).ToList(); - - // Add base path at the beginning of the paths list - if (!string.IsNullOrEmpty(basePath)) - { - space.Insert(0, basePath); - } - - string path = Path.Combine(space.ToArray()); - - return path; - } - - protected ClassDeclarationSyntax AddTargetClassCustomAttributesRoslyn(ClassDeclarationSyntax targetClass, NodeType typeDef) - { - // TODO (svnscha): Change version to given metadata version. - TargetUnit = TargetUnit.AddUsings( - SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("Substrate.NetApi.Model.Types.Metadata.V14")), - SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("Substrate.NetApi.Attributes"))); - - 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/ClientBuilderBaseRoslyn.cs b/Tools/Substrate.DotNet/Service/Node/Base/ClientBuilderBaseRoslyn.cs deleted file mode 100644 index 4719104..0000000 --- a/Tools/Substrate.DotNet/Service/Node/Base/ClientBuilderBaseRoslyn.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using System.CodeDom; -using System.Collections.Generic; - -namespace Substrate.DotNet.Service.Node.Base -{ - public abstract class ClientBuilderBaseRoslyn : BuilderBaseRoslyn - { - public List ModuleNames { get; } - - protected ClientBuilderBaseRoslyn(string projectName, uint id, List moduleNames, NodeTypeResolver typeDict) - : base(projectName, id, typeDict) - { - ModuleNames = moduleNames; - - 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/ClientBuilder.cs b/Tools/Substrate.DotNet/Service/Node/ClientBuilder.cs index d4efbeb..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,88 +23,98 @@ 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; } } diff --git a/Tools/Substrate.DotNet/Service/Node/ClientBuilderRoslyn.cs b/Tools/Substrate.DotNet/Service/Node/ClientBuilderRoslyn.cs deleted file mode 100644 index fb93045..0000000 --- a/Tools/Substrate.DotNet/Service/Node/ClientBuilderRoslyn.cs +++ /dev/null @@ -1,121 +0,0 @@ -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Substrate.DotNet.Service.Node.Base; -using System.Collections.Generic; - -namespace Substrate.DotNet.Service.Node -{ - public class ClientBuilderRoslyn : ClientBuilderBaseRoslyn - { - private ClientBuilderRoslyn(string projectName, uint id, List moduleNames, NodeTypeResolver typeDict) : - base(projectName, id, moduleNames, typeDict) - { - } - - public static ClientBuilderRoslyn Init(string projectName, uint id, List moduleNames, NodeTypeResolver typeDict) - { - return new ClientBuilderRoslyn(projectName, id, moduleNames, typeDict); - } - - public override ClientBuilderRoslyn Create() - { - ClassName = "SubstrateClientExt"; - NamespaceName = $"{ProjectName}.Generated"; - - 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) - { - - // 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 bb54a98..a912d6b 100644 --- a/Tools/Substrate.DotNet/Service/Node/EnumBuilder.cs +++ b/Tools/Substrate.DotNet/Service/Node/EnumBuilder.cs @@ -1,10 +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.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 { @@ -23,258 +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")); + TargetUnit = TargetUnit.AddMembers(typeNamespace); - 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; - } - - 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/EnumBuilderRoslyn.cs b/Tools/Substrate.DotNet/Service/Node/EnumBuilderRoslyn.cs deleted file mode 100644 index b7699dd..0000000 --- a/Tools/Substrate.DotNet/Service/Node/EnumBuilderRoslyn.cs +++ /dev/null @@ -1,127 +0,0 @@ -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.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 -{ - public class EnumBuilderRoslyn : TypeBuilderBaseRoslyn - { - private EnumBuilderRoslyn(string projectName, uint id, NodeTypeVariant typeDef, NodeTypeResolver typeDict) - : base(projectName, id, typeDef, typeDict) - { - } - - public static EnumBuilderRoslyn Init(string projectName, uint id, NodeTypeVariant typeDef, NodeTypeResolver typeDict) - { - return new EnumBuilderRoslyn(projectName, id, typeDef, typeDict); - } - - public override TypeBuilderBaseRoslyn Create() - { - var typeDef = TypeDef as NodeTypeVariant; - string enumName = $"{typeDef.Path.Last()}"; - - if (!Resolver.TypeNames.TryGetValue(Id, out NodeTypeResolved nodeTypeResolved)) - { - throw new NotSupportedException($"Could not find type {Id}"); - } - - ClassName = nodeTypeResolved.ClassName; - - ReferenzName = $"{NamespaceName}.{ClassName}"; - - NamespaceDeclarationSyntax typeNamespace = SyntaxFactory.NamespaceDeclaration(SyntaxFactory.ParseName(NamespaceName)); - - // only add the enumeration in the first variations. - if (string.IsNullOrEmpty(nodeTypeResolved.Name.ClassNameSufix) || nodeTypeResolved.Name.ClassNameSufix == "1") - { - EnumDeclarationSyntax targetType = SyntaxFactory - .EnumDeclaration(enumName) - .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)); - - if (typeDef.Variants != null) - { - 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); - } - - - 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 = targetClass.AddBaseListTypes(SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName($"BaseEnum<{enumName}>"))); - typeNamespace = typeNamespace.AddMembers(targetClass); - } - else - { - 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.FirstOrDefault(p => p.Index == i); - - if (variant == null || variant.TypeFields == null) - { - // add void type - genericTypeArguments.Add(SyntaxFactory.ParseTypeName("BaseVoid")); - } - else - { - if (variant.TypeFields.Length == 1) - { - NodeTypeResolved item = GetFullItemPath(variant.TypeFields[0].TypeId); - genericTypeArguments.Add(SyntaxFactory.ParseTypeName(item.ToString())); - } - else - { - var baseTupleArgs = new List(); - foreach (NodeTypeField field in variant.TypeFields) - { - NodeTypeResolved item = GetFullItemPath(field.TypeId); - baseTupleArgs.Add(SyntaxFactory.ParseTypeName(item.ToString())); - } - GenericNameSyntax baseTuple = SyntaxFactory.GenericName(SyntaxFactory.Identifier("BaseTuple"), SyntaxFactory.TypeArgumentList(SyntaxFactory.SeparatedList(baseTupleArgs))); - genericTypeArguments.Add(baseTuple); - } - } - } - } - else - { - throw new NotImplementedException("Enum extension can't handle such big sized typed rust enumeration, please create a manual fix for it."); - } - - GenericNameSyntax baseEnumExt = SyntaxFactory.GenericName(SyntaxFactory.Identifier("BaseEnumExt"), SyntaxFactory.TypeArgumentList(SyntaxFactory.SeparatedList(genericTypeArguments))); - targetClass = targetClass.AddBaseListTypes(SyntaxFactory.SimpleBaseType(baseEnumExt)); - typeNamespace = typeNamespace.AddMembers(targetClass); - } - - TargetUnit = TargetUnit.AddMembers(typeNamespace); - - return this; - } - } -} \ 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 793fe9f..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,200 +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)))); - } + // 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 - // 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)); - - 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( - GetStorageString(storage.Prefix, entry.Name, entry.StorageType))); + returnValueStr = GetFullItemPath(entry.TypeMap.Item1); - storageMethod.ReturnType = new CodeTypeReference($"async Task<{fullItem}>"); + storageMethod = SyntaxFactory + .MethodDeclaration(SyntaxFactory.ParseTypeName($"Task<{returnValueStr}>"), entry.Name); - storageMethod.Parameters.Add(new CodeParameterDeclarationExpression("CancellationToken", "token")); + parameterMethod = parameterMethod.AddBodyStatements( + SyntaxFactory.ReturnStatement(GetStorageStringRoslyn(storage.Prefix, entry.Name, entry.StorageType))); - CodeMethodInvokeExpression methodInvoke = new( - new CodeTypeReferenceExpression(targetClass.Name), - parameterMethod.Name, Array.Empty()); - - CodeVariableDeclarationStatement variableDeclaration1 = new(typeof(string), "parameters", methodInvoke); - storageMethod.Statements.Add(variableDeclaration1); - - // create result - var resultStatement = new CodeArgumentReferenceExpression(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( - AddPropertyValues(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); + returnValueStr = GetFullItemPath(typeMap.Value); - parameterMethod.Parameters.Add(new CodeParameterDeclarationExpression(key.ToString(), "key")); - parameterMethod.Statements.Add(new CodeMethodReturnStatement( - GetStorageString(storage.Prefix, entry.Name, entry.StorageType, hashers))); + storageMethod = SyntaxFactory + .MethodDeclaration(SyntaxFactory.ParseTypeName($"Task<{returnValueStr}>"), entry.Name); - storageMethod.ReturnType = new CodeTypeReference($"async Task<{value}>"); - storageMethod.Parameters.Add(new CodeParameterDeclarationExpression(key.ToString(), "key")); - storageMethod.Parameters.Add(new CodeParameterDeclarationExpression("CancellationToken", "token")); + parameterMethod = parameterMethod + .AddParameterListParameters(SyntaxFactory.Parameter(SyntaxFactory.Identifier("key")) + .WithType(SyntaxFactory.ParseTypeName(key.ToString()))) + .AddBodyStatements(SyntaxFactory.ReturnStatement(GetStorageStringRoslyn(storage.Prefix, entry.Name, entry.StorageType, hashers))); - CodeMethodInvokeExpression methodInvoke = new( - new CodeTypeReferenceExpression(targetClass.Name), - parameterMethod.Name, - new CodeArgumentReferenceExpression("key")); - CodeVariableDeclarationStatement variableDeclaration = new(typeof(string), "parameters", methodInvoke); - storageMethod.Statements.Add(variableDeclaration); + storageMethod = storageMethod + .AddParameterListParameters(SyntaxFactory.Parameter(SyntaxFactory.Identifier("key")) + .WithType(SyntaxFactory.ParseTypeName(key.ToString()))); - // create result - var resultStatement = new CodeArgumentReferenceExpression(GetInvoceString(value.ToString())); - CodeVariableDeclarationStatement variableDeclaration2 = new("var", "result", resultStatement); - storageMethod.Statements.Add(variableDeclaration2); + ArgumentListSyntax argumentList = SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(new[] { SyntaxFactory.Argument(SyntaxFactory.IdentifierName("key")) })); - - // return statement - storageMethod.Statements.Add( - new CodeMethodReturnStatement( - new CodeVariableReferenceExpression("result"))); + 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) { @@ -230,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) { @@ -251,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"; @@ -407,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) @@ -436,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/ModuleGenBuilderRoslyn.cs b/Tools/Substrate.DotNet/Service/Node/ModuleGenBuilderRoslyn.cs deleted file mode 100644 index 80fab2e..0000000 --- a/Tools/Substrate.DotNet/Service/Node/ModuleGenBuilderRoslyn.cs +++ /dev/null @@ -1,612 +0,0 @@ -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 System; -using System.Collections.Generic; -using System.Linq; - -namespace Substrate.DotNet.Service.Node -{ - public class ModuleGenBuilderRoslyn : ModuleBuilderBaseRoslyn - { - private ModuleGenBuilderRoslyn(string projectName, uint id, PalletModule module, NodeTypeResolver typeDict, Dictionary nodeTypes) : - base(projectName, id, module, typeDict, nodeTypes) - { - } - - public static ModuleGenBuilderRoslyn Init(string projectName, uint id, PalletModule module, NodeTypeResolver typeDict, Dictionary nodeTypes) - { - return new ModuleGenBuilderRoslyn(projectName, id, module, typeDict, nodeTypes); - } - - public override ModuleGenBuilderRoslyn Create() - { - 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; - - NamespaceDeclarationSyntax typeNamespace = SyntaxFactory - .NamespaceDeclaration(SyntaxFactory.ParseName(NamespaceName)); - - typeNamespace = CreateStorage(typeNamespace); - typeNamespace = CreateCalls(typeNamespace); - typeNamespace = CreateEvents(typeNamespace); - typeNamespace = CreateConstants(typeNamespace); - typeNamespace = CreateErrors(typeNamespace); - - TargetUnit = TargetUnit - .AddUsings(usings) - .AddMembers(typeNamespace); - - return this; - } - - private NamespaceDeclarationSyntax CreateStorage(NamespaceDeclarationSyntax typeNamespace) - { - ClassName = Module.Name + "Storage"; - - PalletStorage storage = Module.Storage; - - 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 = 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"; - - // 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 - - MethodDeclarationSyntax storageMethod; - ExpressionStatementSyntax methodInvoke; - NodeTypeResolved returnValueStr; - - if (entry.StorageType == Storage.Type.Plain) - { - returnValueStr = GetFullItemPath(entry.TypeMap.Item1); - - storageMethod = SyntaxFactory - .MethodDeclaration(SyntaxFactory.ParseTypeName($"Task<{returnValueStr}>"), entry.Name); - - parameterMethod = parameterMethod.AddBodyStatements( - SyntaxFactory.ReturnStatement(GetStorageStringRoslyn(storage.Prefix, entry.Name, entry.StorageType))); - - methodInvoke = SyntaxFactory.ExpressionStatement(SyntaxFactory.InvocationExpression(SyntaxFactory.IdentifierName(parameterMethod.Identifier))); - - // add storage key mapping in constructor - 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); - returnValueStr = GetFullItemPath(typeMap.Value); - - 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 = 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 NamespaceDeclarationSyntax CreateCalls(NamespaceDeclarationSyntax namespaceDeclaration) - { - ClassName = Module.Name + "Calls"; - - PalletCalls calls = Module.Calls; - - ClassDeclarationSyntax targetClass = SyntaxFactory.ClassDeclaration(ClassName) - .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.SealedKeyword))); - - if (calls != null) - { - if (NodeTypes.TryGetValue(calls.TypeId, out NodeType nodeType)) - { - var typeDef = nodeType as NodeTypeVariant; - - if (typeDef.Variants != null) - { - foreach (TypeVariant variant in typeDef.Variants) - { - 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 = callMethod.WithLeadingTrivia(GetCommentsRoslyn(typeDef.Docs, null, variant.Name)); - - string byteArrayName = "byteArray"; - - 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) - { - foreach (NodeTypeField field in variant.TypeFields) - { - NodeTypeResolved fullItem = GetFullItemPath(field.TypeId); - - // 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 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 NamespaceDeclarationSyntax CreateEvents(NamespaceDeclarationSyntax namespaceDeclaration) - { - ClassName = Module.Name + "Events"; - - PalletEvents events = Module.Events; - - //if (events != null && NodeTypes.TryGetValue(events.TypeId, out NodeType nodeType)) - //{ - // var typeDef = nodeType as NodeTypeVariant; - - // if (typeDef.Variants != null) - // { - // foreach (TypeVariant variant in typeDef.Variants) - // { - // 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)); - - // QualifiedNameSyntax baseTupleType = SyntaxFactory.QualifiedName(SyntaxFactory.IdentifierName("BaseTuple"), SyntaxFactory.IdentifierName(string.Empty)); - // if (variant.TypeFields != null) - // { - // foreach (NodeTypeField field in variant.TypeFields) - // { - // NodeTypeResolved fullItem = GetFullItemPath(field.TypeId); - // baseTupleType = baseTupleType.WithRight(SyntaxFactory.IdentifierName(fullItem.ToString())); - // } - // } - // eventClass = eventClass.AddBaseListTypes(SyntaxFactory.SimpleBaseType(baseTupleType)); - - // namespaceDeclaration = namespaceDeclaration.AddMembers(eventClass); - // } - // } - //} - - return namespaceDeclaration; - } - - private NamespaceDeclarationSyntax CreateConstants(NamespaceDeclarationSyntax namespaceDeclaration) - { - ClassName = Module.Name + "Constants"; - - PalletConstant[] constants = Module.Constants; - - 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) - { - MethodDeclarationSyntax constantMethod = SyntaxFactory.MethodDeclaration(SyntaxFactory.ParseTypeName("void"), constant.Name) - .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword))); - - // add comment to class if exists - constantMethod = constantMethod.WithLeadingTrivia(GetCommentsRoslyn(constant.Docs, null, constant.Name)); - - targetClass = targetClass.AddMembers(constantMethod); - - if (NodeTypes.TryGetValue(constant.TypeId, out NodeType nodeType)) - { - NodeTypeResolved nodeTypeResolved = GetFullItemPath(nodeType.Id); - constantMethod = constantMethod.WithReturnType(SyntaxFactory.ParseTypeName(nodeTypeResolved.ToString())); - - // assign new result object - 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 - 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 = 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 NamespaceDeclarationSyntax CreateErrors(NamespaceDeclarationSyntax namespaceDeclaration) - { - ClassName = Module.Name + "Errors"; - - PalletErrors errors = Module.Errors; - - if (errors != null) - { - if (NodeTypes.TryGetValue(errors.TypeId, out NodeType nodeType)) - { - var typeDef = nodeType as NodeTypeVariant; - - EnumDeclarationSyntax targetClass = SyntaxFactory.EnumDeclaration(ClassName) - .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword))); - - if (typeDef.Variants != null) - { - foreach (TypeVariant variant in typeDef.Variants) - { - EnumMemberDeclarationSyntax enumField = SyntaxFactory.EnumMemberDeclaration(variant.Name); - - // add comment to field if exists - enumField = enumField.WithLeadingTrivia(GetCommentsRoslyn(variant.Docs, null, variant.Name)); - - targetClass = targetClass.AddMembers(enumField); - } - } - - namespaceDeclaration = namespaceDeclaration.AddMembers(targetClass); - } - } - - return namespaceDeclaration; - } - - private static string GetInvoceString(string returnType) - { - return "await _client.GetStorageAsync<" + returnType + ">(parameters, token)"; - } - - private static InvocationExpressionSyntax GetStorageStringRoslyn(string module, string item, Storage.Type type, Storage.Hasher[] hashers = null) - { - 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) - { - 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 = SyntaxFactory.MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - SyntaxFactory.IdentifierName("key"), - SyntaxFactory.IdentifierName("Value") - ); - } - - 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 SyntaxFactory.InvocationExpression( - SyntaxFactory.MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - SyntaxFactory.IdentifierName("RequestGenerator"), - SyntaxFactory.IdentifierName("GetStorage"))) - .WithArgumentList(SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(codeExpressions))); - } - - private static ExpressionSyntax[] GetStorageMapStringRoslyn(string keyType, string returnType, string module, string item, Storage.Hasher[] hashers = null) - { - 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) - { - 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 ExpressionStatementSyntax AddPropertyValuesRoslyn(ExpressionSyntax[] exprs, string variableReference) - { - 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/Old/ArrayBuilderCodeDom.cs b/Tools/Substrate.DotNet/Service/Node/Old/ArrayBuilderCodeDom.cs new file mode 100644 index 0000000..a6c89b5 --- /dev/null +++ b/Tools/Substrate.DotNet/Service/Node/Old/ArrayBuilderCodeDom.cs @@ -0,0 +1,184 @@ +using Substrate.DotNet.Service.Node.Base; +using Substrate.NetApi.Model.Meta; +using System; +using System.CodeDom; +using System.Linq; +using System.Reflection; + +namespace Substrate.DotNet.Service.Node +{ + public class ArrayBuilderCodeDom : TypeBuilderBaseCodeDom + { + public static int Counter = 0; + + private ArrayBuilderCodeDom(string projectName, uint id, NodeTypeArray typeDef, NodeTypeResolver typeDict) + : base(projectName, id, typeDef, typeDict) + { + } + + private static CodeMemberMethod GetDecode(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++) " + + "{" + + $"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")); + return decodeMethod; + } + + private static CodeMemberMethod GetEncode() + { + 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()")); + return encodeMethod; + } + + public static ArrayBuilderCodeDom Create(string projectName, uint id, NodeTypeArray nodeType, NodeTypeResolver typeDict) + { + return new ArrayBuilderCodeDom(projectName, id, nodeType, typeDict); + } + + public override TypeBuilderBaseCodeDom Create() + { + var typeDef = TypeDef as NodeTypeArray; + + NodeTypeResolved fullItem = GetFullItemPath(typeDef.TypeId); + + ClassName = $"Arr{typeDef.Length}{fullItem.ClassName}"; + + CodeNamespace typeNamespace = new(NamespaceName); + TargetUnit.Namespaces.Add(typeNamespace); + + if (ClassName.Any(ch => !char.IsLetterOrDigit(ch))) + { + 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")); + + // add comment to class if exists + targetClass.Comments.AddRange(GetComments(typeDef.Docs, typeDef)); + AddTargetClassCustomAttributes(targetClass, typeDef); + + typeNamespace.Types.Add(targetClass); + + // 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); + + CodeMemberMethod encodeMethod = ArrayBuilderCodeDom.GetEncode(); + targetClass.Members.Add(encodeMethod); + + CodeMemberMethod decodeMethod = ArrayBuilderCodeDom.GetDecode(fullItem.ToString()); + targetClass.Members.Add(decodeMethod); + + 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/Old/BuilderBaseCodeDom.cs b/Tools/Substrate.DotNet/Service/Node/Old/BuilderBaseCodeDom.cs new file mode 100644 index 0000000..6c3332f --- /dev/null +++ b/Tools/Substrate.DotNet/Service/Node/Old/BuilderBaseCodeDom.cs @@ -0,0 +1,180 @@ +using Substrate.NetApi.Model.Meta; +using System.CodeDom; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Substrate.DotNet.Service.Node.Base +{ + public abstract class BuilderBaseCodeDom + { + public static readonly List Files = new(); + + public uint Id { get; } + + private NodeTypeResolver Resolver { get; } + + public bool Success { get; set; } + + public string NamespaceName { get; protected set; } + + internal string FileName { get; set; } + + public string ClassName { get; set; } + + public string ReferenzName { get; set; } + + public string ProjectName { get; private set; } + + public CodeNamespace ImportsNamespace { get; set; } + + public CodeCompileUnit TargetUnit { get; set; } + + public abstract BuilderBaseCodeDom Create(); + + public BuilderBaseCodeDom(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); + + Success = true; + } + + public NodeTypeResolved GetFullItemPath(uint typeId) + { + 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) + { + 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 (docs != null) + { + foreach (string doc in docs) + { + comments.Add(new CodeCommentStatement(doc, true)); + } + } + + comments.Add(new CodeCommentStatement("", true)); + return comments; + } + + public static CodeMemberMethod SimpleMethod(string name, string returnType = null, object returnExpression = null) + { + CodeMemberMethod nameMethod = new() + { + Attributes = MemberAttributes.Public | MemberAttributes.Override, + Name = name + }; + + if (returnType != null) + { + nameMethod.ReturnType = new CodeTypeReference(returnType); + CodeMethodReturnStatement nameReturnStatement = new() + { + Expression = new CodePrimitiveExpression(returnExpression) + }; + nameMethod.Statements.Add(nameReturnStatement); + } + + return nameMethod; + } + + 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)) + { + // TODO (svnscha) Why does this happen? + // Console.WriteLine($"Overwriting[BUG]: {path}"); + //path += _index++; + } + else + { + Files.Add(path); + } + + using StreamWriter sourceWriter = new(path); + provider.GenerateCodeFromCompileUnit( + TargetUnit, sourceWriter, options); + } + } + + private string GetPath(string basePath) + { + var space = NamespaceName.Split('.').ToList(); + + 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. + space = space.TakeLast(space.Count - 2).ToList(); + + // Add base path at the beginning of the paths list + if (!string.IsNullOrEmpty(basePath)) + { + space.Insert(0, basePath); + } + + string path = Path.Combine(space.ToArray()); + + return path; + } + + protected void AddTargetClassCustomAttributes(CodeTypeDeclaration 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")); + + targetClass.CustomAttributes.Add(new CodeAttributeDeclaration(new CodeTypeReference("SubstrateNodeType"), new CodeAttributeArgument( + new CodeSnippetExpression($"TypeDefEnum.{typeDef.TypeDef}") + ))); + } + } +} \ No newline at end of file diff --git a/Tools/Substrate.DotNet/Service/Node/Old/ClientBuilderBaseCodeDom.cs b/Tools/Substrate.DotNet/Service/Node/Old/ClientBuilderBaseCodeDom.cs new file mode 100644 index 0000000..ae3143f --- /dev/null +++ b/Tools/Substrate.DotNet/Service/Node/Old/ClientBuilderBaseCodeDom.cs @@ -0,0 +1,18 @@ +using System.CodeDom; +using System.Collections.Generic; + +namespace Substrate.DotNet.Service.Node.Base +{ + public abstract class ClientBuilderBaseCodeDom : BuilderBaseCodeDom + { + public List ModuleNames { get; } + + public ClientBuilderBaseCodeDom(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")); + } + } +} \ No newline at end of file diff --git a/Tools/Substrate.DotNet/Service/Node/Old/ClientBuilderCodeDom.cs b/Tools/Substrate.DotNet/Service/Node/Old/ClientBuilderCodeDom.cs new file mode 100644 index 0000000..9a00d3c --- /dev/null +++ b/Tools/Substrate.DotNet/Service/Node/Old/ClientBuilderCodeDom.cs @@ -0,0 +1,114 @@ +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 System.Collections.Generic; +using System.Reflection; + +namespace Substrate.DotNet.Service.Node +{ + public class ClientBuilderCodeDom : ClientBuilderBaseCodeDom + { + private ClientBuilderCodeDom(string projectName, uint id, List moduleNames, NodeTypeResolver typeDict) : + base(projectName, id, moduleNames, typeDict) + { + } + + public static ClientBuilderCodeDom Init(string projectName, uint id, List moduleNames, NodeTypeResolver typeDict) + { + return new ClientBuilderCodeDom(projectName, id, moduleNames, typeDict); + } + + public override ClientBuilderCodeDom 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[] { }))); + + 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)); + } + } + + return this; + } + } +} \ No newline at end of file diff --git a/Tools/Substrate.DotNet/Service/Node/Old/EnumBuilderCodeDom.cs b/Tools/Substrate.DotNet/Service/Node/Old/EnumBuilderCodeDom.cs new file mode 100644 index 0000000..492aaea --- /dev/null +++ b/Tools/Substrate.DotNet/Service/Node/Old/EnumBuilderCodeDom.cs @@ -0,0 +1,280 @@ +using Substrate.DotNet.Service.Node.Base; +using Substrate.NetApi.Model.Meta; +using Substrate.NetApi.Model.Types; +using System; +using System.CodeDom; +using System.Linq; +using System.Reflection; + +namespace Substrate.DotNet.Service.Node +{ + public class EnumBuilderCodeDom : TypeBuilderBaseCodeDom + { + private EnumBuilderCodeDom(string projectName, uint id, NodeTypeVariant typeDef, NodeTypeResolver typeDict) + : base(projectName, id, typeDef, typeDict) + { + } + + public static EnumBuilderCodeDom Init(string projectName, uint id, NodeTypeVariant typeDef, NodeTypeResolver typeDict) + { + return new EnumBuilderCodeDom(projectName, id, typeDef, typeDict); + } + + public override TypeBuilderBaseCodeDom Create() + { + var typeDef = TypeDef as NodeTypeVariant; + + string enumName = $"{typeDef.Path.Last()}"; + + 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 (TypeVariant variant in typeDef.Variants) + { + TargetType.Members.Add(new CodeMemberField(ClassName, variant.Name) + { + InitExpression = new CodePrimitiveExpression(variant.Index) + }); + } + } + typeNamespace.Types.Add(TargetType); + + var targetClass = new CodeTypeDeclaration(ClassName) + { + IsClass = true, + TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed + }; + targetClass.Comments.AddRange(GetComments(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); + } + else + { + var codeTypeRef = new CodeTypeReference("BaseEnumExt"); + codeTypeRef.TypeArguments.Add(new CodeTypeReference(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]; + if (variant == null || variant.TypeFields == null) + { + // add void type + codeTypeRef.TypeArguments.Add(new CodeTypeReference("BaseVoid")); + } + else + { + if (variant.TypeFields.Length == 1) + { + NodeTypeResolved item = GetFullItemPath(variant.TypeFields[0].TypeId); + codeTypeRef.TypeArguments.Add(new CodeTypeReference(item.ToString())); + } + else + { + var baseTuple = new CodeTypeReference("BaseTuple"); + + foreach (NodeTypeField field in variant.TypeFields) + { + NodeTypeResolved item = GetFullItemPath(field.TypeId); + baseTuple.TypeArguments.Add(new CodeTypeReference(item.ToString())); + } + codeTypeRef.TypeArguments.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."); + } + } + + targetClass.BaseTypes.Add(codeTypeRef); + typeNamespace.Types.Add(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; + } + + 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; + } + } +} \ No newline at end of file diff --git a/Tools/Substrate.DotNet/Service/Node/Base/ModuleBuilderBaseRoslyn.cs b/Tools/Substrate.DotNet/Service/Node/Old/ModuleBuilderBaseCodeDom.cs similarity index 78% rename from Tools/Substrate.DotNet/Service/Node/Base/ModuleBuilderBaseRoslyn.cs rename to Tools/Substrate.DotNet/Service/Node/Old/ModuleBuilderBaseCodeDom.cs index 53f545c..2f89537 100644 --- a/Tools/Substrate.DotNet/Service/Node/Base/ModuleBuilderBaseRoslyn.cs +++ b/Tools/Substrate.DotNet/Service/Node/Old/ModuleBuilderBaseCodeDom.cs @@ -4,7 +4,7 @@ namespace Substrate.DotNet.Service.Node.Base { - public abstract class ModuleBuilderBaseRoslyn : BuilderBaseRoslyn + public abstract class ModuleBuilderBaseCodeDom : BuilderBaseCodeDom { public Dictionary NodeTypes { get; private set; } @@ -12,7 +12,7 @@ public abstract class ModuleBuilderBaseRoslyn : BuilderBaseRoslyn public string PrefixName { get; private set; } - protected ModuleBuilderBaseRoslyn(string projectName, uint id, PalletModule module, NodeTypeResolver typeDict, + public ModuleBuilderBaseCodeDom(string projectName, uint id, PalletModule module, NodeTypeResolver typeDict, Dictionary nodeTypes) : base(projectName, id, typeDict) { diff --git a/Tools/Substrate.DotNet/Service/Node/Old/ModuleGenBuilderCodeDom.cs b/Tools/Substrate.DotNet/Service/Node/Old/ModuleGenBuilderCodeDom.cs new file mode 100644 index 0000000..ed54503 --- /dev/null +++ b/Tools/Substrate.DotNet/Service/Node/Old/ModuleGenBuilderCodeDom.cs @@ -0,0 +1,535 @@ +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 +{ + public class ModuleGenBuilderCodeDom : ModuleBuilderBaseCodeDom + { + private ModuleGenBuilderCodeDom(string projectName, uint id, PalletModule module, NodeTypeResolver typeDict, Dictionary nodeTypes) : + base(projectName, id, module, typeDict, nodeTypes) + { + } + + public static ModuleGenBuilderCodeDom Init(string projectName, uint id, PalletModule module, NodeTypeResolver typeDict, Dictionary nodeTypes) + { + return new ModuleGenBuilderCodeDom(projectName, id, module, typeDict, nodeTypes); + } + + public override ModuleGenBuilderCodeDom 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")); + + FileName = "Main" + Module.Name; + NamespaceName = $"{ProjectName}.Generated.Storage"; + ReferenzName = NamespaceName; + + CodeNamespace typeNamespace = new(NamespaceName); + TargetUnit.Namespaces.Add(typeNamespace); + + // add constructor + CodeConstructor constructor = new() + { + Attributes = MemberAttributes.Public | MemberAttributes.Final + }; + + CreateStorage(typeNamespace, constructor); + CreateCalls(typeNamespace); + CreateEvents(typeNamespace); + CreateConstants(typeNamespace); + CreateErrors(typeNamespace); + + return this; + } + + private void CreateStorage(CodeNamespace typeNamespace, CodeConstructor constructor) + { + 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); + + // 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); + + 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)); + + targetClass.Members.Add(storageMethod); + + if (entry.StorageType == Storage.Type.Plain) + { + NodeTypeResolved fullItem = GetFullItemPath(entry.TypeMap.Item1); + + parameterMethod.Statements.Add(new CodeMethodReturnStatement( + GetStorageString(storage.Prefix, entry.Name, entry.StorageType))); + + storageMethod.ReturnType = new CodeTypeReference($"async Task<{fullItem}>"); + + storageMethod.Parameters.Add(new CodeParameterDeclarationExpression("CancellationToken", "token")); + + CodeMethodInvokeExpression methodInvoke = new( + new CodeTypeReferenceExpression(targetClass.Name), + parameterMethod.Name, Array.Empty()); + + CodeVariableDeclarationStatement variableDeclaration1 = new(typeof(string), "parameters", methodInvoke); + storageMethod.Statements.Add(variableDeclaration1); + + // create result + var resultStatement = new CodeArgumentReferenceExpression(GetInvoceString(fullItem.ToString())); + CodeVariableDeclarationStatement variableDeclaration2 = new("var", "result", resultStatement); + storageMethod.Statements.Add(variableDeclaration2); + + // return statement + storageMethod.Statements.Add( + new CodeMethodReturnStatement( + new CodeVariableReferenceExpression("result"))); + + // add storage key mapping in constructor + constructor.Statements.Add( + AddPropertyValues(GetStorageMapString("", fullItem.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( + 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 CodeArgumentReferenceExpression("key")); + CodeVariableDeclarationStatement variableDeclaration = new(typeof(string), "parameters", methodInvoke); + storageMethod.Statements.Add(variableDeclaration); + + // create result + var resultStatement = new CodeArgumentReferenceExpression(GetInvoceString(value.ToString())); + CodeVariableDeclarationStatement variableDeclaration2 = new("var", "result", resultStatement); + storageMethod.Statements.Add(variableDeclaration2); + + + // return statement + storageMethod.Statements.Add( + new CodeMethodReturnStatement( + new CodeVariableReferenceExpression("result"))); + + // add storage key mapping in constructor + constructor.Statements.Add(ModuleGenBuilderCodeDom.AddPropertyValues(ModuleGenBuilderCodeDom.GetStorageMapString(key.ToString(), value.ToString(), storage.Prefix, entry.Name, hashers), "_client.StorageKeyDict")); + } + else + { + throw new NotImplementedException(); + } + } + } + } + + private void CreateCalls(CodeNamespace typeNamespace) + { + ClassName = Module.Name + "Calls"; + + PalletCalls calls = Module.Calls; + + var targetClass = new CodeTypeDeclaration(ClassName) + { + IsClass = true, + TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed + }; + typeNamespace.Types.Add(targetClass); + + if (calls != null) + { + if (NodeTypes.TryGetValue(calls.TypeId, out NodeType nodeType)) + { + var typeDef = nodeType as NodeTypeVariant; + + if (typeDef.Variants != null) + { + 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) + }; + + // add comment to class if exists + callMethod.Comments.AddRange(GetComments(typeDef.Docs, null, variant.Name)); + + string byteArrayName = "byteArray"; + + callMethod.Statements.Add(new CodeVariableDeclarationStatement( + typeof(List), byteArrayName, new CodeObjectCreateExpression("List", Array.Empty()))); + + if (variant.TypeFields != null) + { + foreach (NodeTypeField field in variant.TypeFields) + { + 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"))); + } + } + + // 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); + } + } + } + } + } + + private void CreateEvents(CodeNamespace typeNamespace) + { + ClassName = Module.Name + "Events"; + + PalletEvents events = Module.Events; + + //if (events != null) + //{ + // if (NodeTypes.TryGetValue(events.TypeId, out NodeType nodeType)) + // { + // var typeDef = nodeType as NodeTypeVariant; + + // if (typeDef.Variants != null) + // { + // 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 + // // 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); + // } + // } + // } + //} + } + + private void CreateConstants(CodeNamespace typeNamespace) + { + ClassName = Module.Name + "Constants"; + + PalletConstant[] constants = Module.Constants; + + var targetClass = new CodeTypeDeclaration(ClassName) + { + IsClass = true, + TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed + }; + typeNamespace.Types.Add(targetClass); + + if (constants != null && constants.Any()) + { + foreach (PalletConstant constant in constants) + { + // async Task + CodeMemberMethod constantMethod = new() + { + Attributes = MemberAttributes.Public | MemberAttributes.Final, + Name = constant.Name, + }; + // add comment to class if exists + constantMethod.Comments.AddRange(GetComments(constant.Docs, null, constant.Name)); + + targetClass.Members.Add(constantMethod); + + if (NodeTypes.TryGetValue(constant.TypeId, out NodeType nodeType)) + { + NodeTypeResolved nodeTypeResolved = GetFullItemPath(nodeType.Id); + constantMethod.ReturnType = new CodeTypeReference(nodeTypeResolved.ToString()); + + // assign new result object + CodeVariableDeclarationStatement newStatement = new("var", "result", new CodeObjectCreateExpression(nodeTypeResolved.ToString(), Array.Empty())); + constantMethod.Statements.Add(newStatement); + + // 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); + + // return statement + constantMethod.Statements.Add( + new CodeMethodReturnStatement( + new CodeVariableReferenceExpression("result"))); + } + } + } + } + + private void CreateErrors(CodeNamespace typeNamespace) + { + ClassName = Module.Name + "Errors"; + + PalletErrors errors = Module.Errors; + + if (errors != null) + { + if (NodeTypes.TryGetValue(errors.TypeId, out NodeType nodeType)) + { + var typeDef = nodeType as NodeTypeVariant; + + var targetClass = new CodeTypeDeclaration(ClassName) + { + IsEnum = true, + TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed + }; + + if (typeDef.Variants != null) + { + foreach (TypeVariant variant in typeDef.Variants) + { + var enumField = new CodeMemberField(ClassName, variant.Name); + + // add comment to field if exists + enumField.Comments.AddRange(GetComments(variant.Docs, null, variant.Name)); + + targetClass.Members.Add(enumField); + } + } + + typeNamespace.Types.Add(targetClass); + } + } + } + + 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) + { + var codeExpressions = + new CodeExpression[] { + new CodePrimitiveExpression(module), + new CodePrimitiveExpression(item), + new CodePropertyReferenceExpression(new CodeTypeReferenceExpression(typeof(Storage.Type)), 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") + }); + + if (hashers.Length > 1) + { + keyReference = new CodeSnippetExpression("key.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 + }; + } + + return new CodeMethodInvokeExpression(new CodeTypeReferenceExpression("RequestGenerator"), "GetStorage", codeExpressions); + } + + private static CodeExpression[] GetStorageMapString(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}) + }; + + // 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 + }) + }; + } + + return result; + } + + private static CodeStatement AddPropertyValues(CodeExpression[] exprs, string variableReference) + { + return new CodeExpressionStatement( + new CodeMethodInvokeExpression( + new CodeMethodReferenceExpression( + new CodeTypeReferenceExpression( + new CodeTypeReference(variableReference)), "Add"), exprs)); + } + } +} \ No newline at end of file diff --git a/Tools/Substrate.DotNet/Service/Node/Old/RestServiceControllerModuleBuilderCodeDom.cs b/Tools/Substrate.DotNet/Service/Node/Old/RestServiceControllerModuleBuilderCodeDom.cs new file mode 100644 index 0000000..07174b1 --- /dev/null +++ b/Tools/Substrate.DotNet/Service/Node/Old/RestServiceControllerModuleBuilderCodeDom.cs @@ -0,0 +1,195 @@ +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 RestServiceControllerModuleBuilderCodeDom : ModuleBuilderBaseCodeDom + { + private string NetApiProjectName { get; } + + private RestServiceControllerModuleBuilderCodeDom(string projectName, string netApiProjectName, uint id, PalletModule module, NodeTypeResolver typeDict, Dictionary nodeTypes) : + base(projectName, id, module, typeDict, nodeTypes) + { + NetApiProjectName = netApiProjectName; + } + + public static RestServiceControllerModuleBuilderCodeDom Init(string projectName, string netApiProjectName, uint id, PalletModule module, NodeTypeResolver typeDict, Dictionary nodeTypes) + { + return new RestServiceControllerModuleBuilderCodeDom(projectName, netApiProjectName, id, module, typeDict, nodeTypes); + } + + public override RestServiceControllerModuleBuilderCodeDom 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); + + CreateController(typeNamespace); + + return this; + } + + private void CreateController(CodeNamespace typeNamespace) + { + ClassName = FileName; + + var targetClass = new CodeTypeDeclaration(ClassName) + { + IsClass = true, + TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed + }; + targetClass.BaseTypes.Add(new CodeTypeReference("ControllerBase")); + targetClass.Comments.AddRange(GetComments(new string[] { $"{ClassName} controller to access storages." })); + + typeNamespace.Types.Add(targetClass); + targetClass.CustomAttributes.Add( + new CodeAttributeDeclaration("ApiController")); + targetClass.CustomAttributes.Add( + new CodeAttributeDeclaration("Route", + new CodeAttributeArgument[] { + new CodeAttributeArgument(new CodePrimitiveExpression("[controller]")) + })); + + string fieldName = $"{Module.Storage.Prefix}Storage"; + CodeMemberField field = new() + { + Attributes = MemberAttributes.Private, + Name = fieldName.MakePrivateField(), + Type = new CodeTypeReference($"I{fieldName}") + }; + targetClass.Members.Add(field); + + CodeConstructor constructor = new() + { + Attributes = + MemberAttributes.Public | MemberAttributes.Final + }; + targetClass.Members.Add(constructor); + constructor.Comments.AddRange(GetComments(new string[] { $"{ClassName} constructor." })); + + constructor.Parameters.Add(new CodeParameterDeclarationExpression($"I{fieldName}", fieldName.MakePublicField())); + + // constructor initialize storage properties + constructor.Statements.Add(new CodeAssignStatement( + new CodeVariableReferenceExpression(field.Name), + new CodeVariableReferenceExpression(fieldName.MakePublicField()))); + + if (Module.Storage.Entries != null) + { + 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); + } + } + } + } +} \ No newline at end of file diff --git a/Tools/Substrate.DotNet/Service/Node/Old/RestServiceStorageModuleBuilderCodeDom.cs b/Tools/Substrate.DotNet/Service/Node/Old/RestServiceStorageModuleBuilderCodeDom.cs new file mode 100644 index 0000000..adc033a --- /dev/null +++ b/Tools/Substrate.DotNet/Service/Node/Old/RestServiceStorageModuleBuilderCodeDom.cs @@ -0,0 +1,253 @@ +using Substrate.DotNet.Extensions; +using Substrate.DotNet.Service.Node.Base; +using Substrate.NetApi.Model.Meta; +using Substrate.ServiceLayer.Storage; +using System; +using System.CodeDom; +using System.Collections.Generic; +using System.Reflection; + +namespace Substrate.DotNet.Service.Node +{ + public class RestServiceStorageModuleBuilderCodeDom : ModuleBuilderBaseCodeDom + { + private RestServiceStorageModuleBuilderCodeDom(string projectName, uint id, PalletModule module, NodeTypeResolver typeDict, Dictionary nodeTypes) : + base(projectName, id, module, typeDict, nodeTypes) + { + } + + public static RestServiceStorageModuleBuilderCodeDom Init(string projectName, uint id, PalletModule module, NodeTypeResolver typeDict, Dictionary nodeTypes) + { + return new RestServiceStorageModuleBuilderCodeDom(projectName, id, module, typeDict, nodeTypes); + } + + public override RestServiceStorageModuleBuilderCodeDom 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); + + CreateStorage(typeNamespace); + return this; + } + + private void CreateStorage(CodeNamespace typeNamespace) + { + 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)); + + typeNamespace.Types.Add(targetClass); + + CodeConstructor constructor = new() + { + 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." })); + + targetClass.Members.Add(constructor); + + CodeMemberMethod initializeAsyncMethod = new() + { + Attributes = MemberAttributes.Public | MemberAttributes.Final, + Name = $"InitializeAsync", + ReturnType = new CodeTypeReference("async Task") + }; + 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." })); + + if (Module.Storage.Entries != null) + { + var keyParamter = new CodeParameterDeclarationExpression(typeof(string), "key"); + var dataParamter = new CodeParameterDeclarationExpression(typeof(string), "data"); + + foreach (Entry entry in Module.Storage.Entries) + { + CodeTypeReference baseReturnType; + CodeTypeReference returnType; + CodeExpression[] 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()}>"); + + updateExpression = new CodeExpression[] { + new CodeVariableReferenceExpression(dataParamter.Name)}; + tryGetExpression = Array.Empty(); + } + 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); + 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 + } + }; + } + else + { + throw new NotImplementedException(); + } + + // create typed storage field + CodeMemberField field = new() + { + Attributes = MemberAttributes.Private, + Name = $"{entry.Name.MakePrivateField()}TypedStorage", + Type = returnType + }; + + field.Comments.AddRange(GetComments(new string[] { $"{field.Name} typed storage field" })); + targetClass.Members.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); + + // 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") + }))); + + // 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); + + // create on update + CodeMemberMethod onUpdateMethod = new() + { + Attributes = MemberAttributes.Public | MemberAttributes.Final, + Name = $"OnUpdate{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)); + + targetInterface.Members.Add(getStorageMethod); + + if (tryGetExpression.Length == 0) + { + onUpdateMethod.Parameters.Add(dataParamter); + + getStorageMethod.Statements.Add(new CodeMethodReturnStatement( + new CodeMethodInvokeExpression( + new CodeVariableReferenceExpression(prop.Name), + "Get", Array.Empty()))); + } + 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)) })); + } + + targetClass.Members.Add(getStorageMethod); + } + } + } + } +} \ No newline at end of file diff --git a/Tools/Substrate.DotNet/Service/Node/Old/StructBuilderCodeDom.cs b/Tools/Substrate.DotNet/Service/Node/Old/StructBuilderCodeDom.cs new file mode 100644 index 0000000..bf521cf --- /dev/null +++ b/Tools/Substrate.DotNet/Service/Node/Old/StructBuilderCodeDom.cs @@ -0,0 +1,198 @@ +using Substrate.DotNet.Extensions; +using Substrate.DotNet.Service.Node.Base; +using Substrate.NetApi.Model.Meta; +using System.CodeDom; +using System.Linq; +using System.Reflection; + +namespace Substrate.DotNet.Service.Node +{ + public class StructBuilderCodeDom : TypeBuilderBaseCodeDom + { + private StructBuilderCodeDom(string projectName, uint id, NodeTypeComposite typeDef, NodeTypeResolver typeDict) + : base(projectName, id, typeDef, typeDict) + { + } + + private static CodeMemberField GetPropertyField(string name, string baseType) + { + CodeMemberField field = new() + { + Attributes = MemberAttributes.Private, + Name = name.MakePrivateField(), + Type = new CodeTypeReference($"{baseType}") + }; + return field; + } + + private static CodeMemberProperty GetProperty(string name, CodeMemberField 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())); + return prop; + } + + private CodeMemberMethod GetDecode(NodeTypeField[] typeFields) + { + 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")); + + if (typeFields != null) + { + for (int i = 0; i < typeFields.Length; i++) + { + NodeTypeField typeField = typeFields[i]; + + string fieldName = StructBuilderCodeDom.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.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)")); + + return decodeMethod; + } + + private static CodeMemberMethod GetEncode(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()")); + + if (typeFields != null) + { + for (int i = 0; i < typeFields.Length; i++) + { + NodeTypeField typeField = typeFields[i]; + string fieldName = StructBuilderCodeDom.GetFieldName(typeField, "value", typeFields.Length, i); + + encodeMethod.Statements.Add(new CodeSnippetExpression($"result.AddRange({fieldName.MakeMethod()}.Encode())")); + } + } + + encodeMethod.Statements.Add(new CodeSnippetExpression("return result.ToArray()")); + return encodeMethod; + } + + public static BuilderBaseCodeDom Init(string projectName, uint id, NodeTypeComposite typeDef, NodeTypeResolver typeDict) + { + return new StructBuilderCodeDom(projectName, id, typeDef, typeDict); + } + + public override TypeBuilderBaseCodeDom Create() + { + var typeDef = TypeDef as NodeTypeComposite; + + ClassName = $"{typeDef.Path.Last()}"; + + ReferenzName = $"{NamespaceName}.{typeDef.Path.Last()}"; + + 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")); + + // 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); + + if (typeDef.TypeFields != null) + { + for (int i = 0; i < typeDef.TypeFields.Length; i++) + { + NodeTypeField typeField = typeDef.TypeFields[i]; + string fieldName = StructBuilderCodeDom.GetFieldName(typeField, "value", typeDef.TypeFields.Length, i); + + NodeTypeResolved fullItem = GetFullItemPath(typeField.TypeId); + + CodeMemberField field = StructBuilderCodeDom.GetPropertyField(fieldName, fullItem.ToString()); + + // add comment to field if exists + field.Comments.AddRange(GetComments(typeField.Docs, null, fieldName)); + + targetClass.Members.Add(field); + targetClass.Members.Add(StructBuilderCodeDom.GetProperty(fieldName, field)); + } + } + + CodeMemberMethod encodeMethod = StructBuilderCodeDom.GetEncode(typeDef.TypeFields); + targetClass.Members.Add(encodeMethod); + + CodeMemberMethod decodeMethod = GetDecode(typeDef.TypeFields); + targetClass.Members.Add(decodeMethod); + + return this; + } + + private static string GetFieldName(NodeTypeField typeField, string alterName, int length, int index) + { + if (typeField.Name == null) + { + if (length > 1) + { + if (typeField.TypeName == null) + { + return alterName + index; + } + else + { + return typeField.TypeName; + } + } + else + { + return alterName; + } + } + else + { + return typeField.Name; + } + } + } +} \ No newline at end of file diff --git a/Tools/Substrate.DotNet/Service/Node/Base/TypeBuilderBaseRoslyn.cs b/Tools/Substrate.DotNet/Service/Node/Old/TypeBuilderBaseCodeDom.cs similarity index 60% rename from Tools/Substrate.DotNet/Service/Node/Base/TypeBuilderBaseRoslyn.cs rename to Tools/Substrate.DotNet/Service/Node/Old/TypeBuilderBaseCodeDom.cs index 6ddf7ca..78939ce 100644 --- a/Tools/Substrate.DotNet/Service/Node/Base/TypeBuilderBaseRoslyn.cs +++ b/Tools/Substrate.DotNet/Service/Node/Old/TypeBuilderBaseCodeDom.cs @@ -2,11 +2,11 @@ namespace Substrate.DotNet.Service.Node.Base { - public abstract class TypeBuilderBaseRoslyn : BuilderBaseRoslyn + public abstract class TypeBuilderBaseCodeDom : BuilderBaseCodeDom { public NodeType TypeDef { get; } - public TypeBuilderBaseRoslyn(string projectName, uint id, NodeType typeDef, NodeTypeResolver resolver) + public TypeBuilderBaseCodeDom(string projectName, uint id, NodeType typeDef, NodeTypeResolver resolver) : base(projectName, id, resolver) { TypeDef = typeDef; diff --git a/Tools/Substrate.DotNet/Service/Node/RestServiceControllerModuleBuilder.cs b/Tools/Substrate.DotNet/Service/Node/RestServiceControllerModuleBuilder.cs index 1fb34b4..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 { @@ -31,165 +35,211 @@ public override RestServiceControllerModuleBuilder Create() 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"))); - CreateController(typeNamespace); + NamespaceDeclarationSyntax namespaceDeclaration = SyntaxFactory + .NamespaceDeclaration(SyntaxFactory.ParseName(NamespaceName)); + + CreateController(namespaceDeclaration); + + 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 - }; - targetClass.BaseTypes.Add(new CodeTypeReference("ControllerBase")); - targetClass.Comments.AddRange(GetComments(new string[] { $"{ClassName} controller to access storages." })); - - typeNamespace.Types.Add(targetClass); - targetClass.CustomAttributes.Add( - new CodeAttributeDeclaration("ApiController")); - targetClass.CustomAttributes.Add( - new CodeAttributeDeclaration("Route", - new CodeAttributeArgument[] { - new CodeAttributeArgument(new CodePrimitiveExpression("[controller]")) - })); + 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)); - string fieldName = $"{Module.Storage.Prefix}Storage"; - CodeMemberField field = new() + targetClass = targetClass.AddMembers(methodDeclaration); + } + } + + namespaceDeclaration = namespaceDeclaration.AddMembers(targetClass); + + TargetUnit = TargetUnit.AddMembers(namespaceDeclaration); + } + + 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/RestServiceControllerModuleBuilderRoslyn.cs b/Tools/Substrate.DotNet/Service/Node/RestServiceControllerModuleBuilderRoslyn.cs deleted file mode 100644 index 86e21f5..0000000 --- a/Tools/Substrate.DotNet/Service/Node/RestServiceControllerModuleBuilderRoslyn.cs +++ /dev/null @@ -1,245 +0,0 @@ -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 -{ - public class RestServiceControllerModuleBuilderRoslyn : ModuleBuilderBaseRoslyn - { - private string NetApiProjectName { get; } - - private RestServiceControllerModuleBuilderRoslyn(string projectName, string netApiProjectName, uint id, PalletModule module, NodeTypeResolver typeDict, Dictionary nodeTypes) : - base(projectName, id, module, typeDict, nodeTypes) - { - NetApiProjectName = netApiProjectName; - } - - public static RestServiceControllerModuleBuilderRoslyn Init(string projectName, string netApiProjectName, uint id, PalletModule module, NodeTypeResolver typeDict, Dictionary nodeTypes) - { - return new RestServiceControllerModuleBuilderRoslyn(projectName, netApiProjectName, id, module, typeDict, nodeTypes); - } - - public override RestServiceControllerModuleBuilderRoslyn Create() - { - if (Module.Storage == null) - { - Success = false; - return this; - } - - FileName = Module.Storage.Prefix + "Controller"; - ReferenzName = $"{ProjectName}.Generated.Controller.{FileName}"; - NamespaceName = $"{ProjectName}.Generated.Controller"; - - 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); - - TargetUnit = TargetUnit.AddUsings(usingDirectives.ToArray()); - - return this; - } - - private void CreateController(NamespaceDeclarationSyntax namespaceDeclaration) - { - ClassName = FileName; - - // 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) - { - 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 = targetClass.AddMembers(methodDeclaration); - } - } - - namespaceDeclaration = namespaceDeclaration.AddMembers(targetClass); - - TargetUnit = TargetUnit.AddMembers(namespaceDeclaration); - } - - 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) - { - 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"))); - - invokeExpression = SyntaxFactory.IdentifierName("key"); - } - else - { - throw new NotImplementedException(); - } - - string indentifier = $"Get{entry.Name}"; - - 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))))))))))); - - - - // Add parameters to method - foreach (ParameterSyntax parameter in methodParameters) - { - 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 330b8e2..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 { @@ -29,86 +35,96 @@ public override RestServiceStorageModuleBuilder Create() 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); + // 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" })); - var targetClass = new CodeTypeDeclaration(ClassName) + if (Module.Storage.Entries != null) { - IsClass = true, - TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed - }; - - targetClass.Comments.AddRange(GetComments(new string[] { $"{ClassName} class definition." })); - targetClass.BaseTypes.Add(new CodeTypeReference(targetInterface.Name)); + ParameterSyntax keyParamter = SyntaxFactory.Parameter(SyntaxFactory.Identifier("key")) + .WithType(SyntaxFactory.ParseTypeName("string")); - typeNamespace.Types.Add(targetClass); + ParameterSyntax dataParamter = SyntaxFactory.Parameter(SyntaxFactory.Identifier("data")) + .WithType(SyntaxFactory.ParseTypeName("string")); - CodeConstructor constructor = new() - { - Attributes = MemberAttributes.Public | MemberAttributes.Final, - }; + var constructorStatements = new List(); + var initializeStatements = new List(); - 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." })); + //var fields = new List(); + var props = new List(); - targetClass.Members.Add(constructor); - - CodeMemberMethod initializeAsyncMethod = new() - { - Attributes = MemberAttributes.Public | MemberAttributes.Final, - Name = $"InitializeAsync", - ReturnType = new CodeTypeReference("async Task") - }; - 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." })); - - 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) { @@ -116,138 +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}", - }; - - 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}" })); + 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}" })); - 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/RestServiceStorageModuleBuilderRoslyn.cs b/Tools/Substrate.DotNet/Service/Node/RestServiceStorageModuleBuilderRoslyn.cs deleted file mode 100644 index 9afc154..0000000 --- a/Tools/Substrate.DotNet/Service/Node/RestServiceStorageModuleBuilderRoslyn.cs +++ /dev/null @@ -1,330 +0,0 @@ -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; -using System; -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 -{ - public class RestServiceStorageModuleBuilderRoslyn : ModuleBuilderBaseRoslyn - { - private RestServiceStorageModuleBuilderRoslyn(string projectName, uint id, PalletModule module, NodeTypeResolver typeDict, Dictionary nodeTypes) : - base(projectName, id, module, typeDict, nodeTypes) - { - } - - public static RestServiceStorageModuleBuilderRoslyn Init(string projectName, uint id, PalletModule module, NodeTypeResolver typeDict, Dictionary nodeTypes) - { - return new RestServiceStorageModuleBuilderRoslyn(projectName, id, module, typeDict, nodeTypes); - } - - public override RestServiceStorageModuleBuilderRoslyn Create() - { - if (Module.Storage == null) - { - Success = false; - return this; - } - - FileName = Module.Storage.Prefix + "Storage"; - ReferenzName = $"{ProjectName}.Generated.Storage.{FileName}"; - NamespaceName = $"{ProjectName}.Generated.Storage"; - - 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)); - - return this; - } - - private NamespaceDeclarationSyntax CreateStorage(NamespaceDeclarationSyntax namespaceDeclaration) - { - // Setting ClassName - ClassName = Module.Storage.Prefix + "Storage"; - - // 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" })); - - if (Module.Storage.Entries != null) - { - ParameterSyntax keyParamter = SyntaxFactory.Parameter(SyntaxFactory.Identifier("key")) - .WithType(SyntaxFactory.ParseTypeName("string")); - - ParameterSyntax dataParamter = SyntaxFactory.Parameter(SyntaxFactory.Identifier("data")) - .WithType(SyntaxFactory.ParseTypeName("string")); - - var constructorStatements = new List(); - var initializeStatements = new List(); - - //var fields = new List(); - var props = new List(); - - var interfaceMethods = new List(); - var methodOnAndGet = new List(); - - foreach (Entry entry in Module.Storage.Entries) - { - TypeSyntax baseReturnType; - TypeSyntax returnType; - ArgumentListSyntax updateExpression, tryGetExpression; - - if (entry.StorageType == Storage.Type.Plain) - { - NodeTypeResolved fullItem = GetFullItemPath(entry.TypeMap.Item1); - baseReturnType = SyntaxFactory.ParseTypeName(fullItem.ToString()); - returnType = SyntaxFactory.ParseTypeName($"TypedStorage<{fullItem}>"); - - updateExpression = SyntaxFactory.ArgumentList().AddArguments( - SyntaxFactory.Argument(SyntaxFactory.IdentifierName(dataParamter.Identifier))); - - tryGetExpression = SyntaxFactory.ArgumentList(); - } - 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); - 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(); - } - - string fieldName = $"{entry.Name.MakePrivateField()}TypedStorage"; - - // 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 - 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 - 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 - 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 - 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}" })); - - - // create get and gets - MethodDeclarationSyntax getInterfaceMethod = SyntaxFactory - .MethodDeclaration(baseReturnType, $"Get{entry.Name}") - .WithLeadingTrivia(GetCommentsRoslyn(entry.Docs, null, entry.Name)) - .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)); - - MethodDeclarationSyntax getStorageMethod = SyntaxFactory - .MethodDeclaration(baseReturnType, $"Get{entry.Name}") - .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) - .WithLeadingTrivia(GetCommentsRoslyn(entry.Docs, null, entry.Name)); - - if (tryGetExpression.Arguments.Count == 0) - { - 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 = 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)))))); - - } - - 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/StructBuilder.cs b/Tools/Substrate.DotNet/Service/Node/StructBuilder.cs index df56751..6564b54 100644 --- a/Tools/Substrate.DotNet/Service/Node/StructBuilder.cs +++ b/Tools/Substrate.DotNet/Service/Node/StructBuilder.cs @@ -1,9 +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 { @@ -14,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) { @@ -69,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) { @@ -101,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; } @@ -120,51 +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)); + + PropertyDeclarationSyntax propertyDeclaration = GetPropertyRoslyn(fieldName, SyntaxFactory.ParseTypeName(fullItem.ToString())); + propertyDeclaration = propertyDeclaration.WithLeadingTrivia(GetCommentsRoslyn(typeField.Docs, null, fieldName)); - targetClass.Members.Add(field); - targetClass.Members.Add(StructBuilder.GetProperty(fieldName, field)); + 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; } diff --git a/Tools/Substrate.DotNet/Service/Node/StructBuilderRoslyn.cs b/Tools/Substrate.DotNet/Service/Node/StructBuilderRoslyn.cs deleted file mode 100644 index 627625f..0000000 --- a/Tools/Substrate.DotNet/Service/Node/StructBuilderRoslyn.cs +++ /dev/null @@ -1,216 +0,0 @@ -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.Collections.Generic; -using System.Linq; - -namespace Substrate.DotNet.Service.Node -{ - public class StructBuilderRoslyn : TypeBuilderBaseRoslyn - { - private StructBuilderRoslyn(string projectName, uint id, NodeTypeComposite typeDef, NodeTypeResolver typeDict) - : base(projectName, id, typeDef, typeDict) - { - } - - private static FieldDeclarationSyntax GetPropertyFieldRoslyn(string name, string baseType) - { - FieldDeclarationSyntax fieldDeclaration = SyntaxFactory.FieldDeclaration( - SyntaxFactory.VariableDeclaration( - SyntaxFactory.ParseTypeName(baseType), - SyntaxFactory.SingletonSeparatedList( - SyntaxFactory.VariableDeclarator(name.MakePrivateField())))) - .AddModifiers(SyntaxFactory.Token(SyntaxKind.PrivateKeyword)); - - return fieldDeclaration; - } - - private static PropertyDeclarationSyntax GetPropertyWithFieldRoslyn(string name, FieldDeclarationSyntax propertyField) - { - 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 static PropertyDeclarationSyntax GetPropertyRoslyn(string name, TypeSyntax type) - { - 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) - { - for (int i = 0; i < typeFields.Length; i++) - { - NodeTypeField typeField = typeFields[i]; - - string fieldName = GetFieldName(typeField, "value", typeFields.Length, i); - NodeTypeResolved fullItem = GetFullItemPath(typeField.TypeId); - - decodeMethod = decodeMethod.AddBodyStatements(SyntaxFactory.ParseStatement($"{fieldName.MakeMethod()} = new {fullItem}();")); - decodeMethod = decodeMethod.AddBodyStatements(SyntaxFactory.ParseStatement($"{fieldName.MakeMethod()}.Decode(byteArray, ref p);")); - } - } - - 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 MethodDeclarationSyntax GetEncodeRoslyn(NodeTypeField[] typeFields) - { - 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) - { - for (int i = 0; i < typeFields.Length; i++) - { - NodeTypeField typeField = typeFields[i]; - string fieldName = StructBuilderRoslyn.GetFieldName(typeField, "value", typeFields.Length, i); - - encodeMethod = encodeMethod.AddBodyStatements(SyntaxFactory.ParseStatement($"result.AddRange({fieldName.MakeMethod()}.Encode());")); - } - } - - encodeMethod = encodeMethod.AddBodyStatements(SyntaxFactory.ParseStatement("return result.ToArray();")); - - return encodeMethod; - } - - public static BuilderBaseRoslyn Init(string projectName, uint id, NodeTypeComposite typeDef, NodeTypeResolver typeDict) - { - return new StructBuilderRoslyn(projectName, id, typeDef, typeDict); - } - - public override TypeBuilderBaseRoslyn Create() - { - var typeDef = TypeDef as NodeTypeComposite; - - ClassName = $"{typeDef.Path.Last()}"; - - ReferenzName = $"{NamespaceName}.{ClassName}"; - - 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 = 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 = GetFieldName(typeField, "value", typeDef.TypeFields.Length, i); - - NodeTypeResolved fullItem = GetFullItemPath(typeField.TypeId); - - //FieldDeclarationSyntax field = GetPropertyFieldRoslyn(fieldName, fullItem.ToString()); - // add comment to field if exists - //field = field.WithLeadingTrivia(GetCommentsRoslyn(typeField.Docs, null, fieldName)); - //targetClass = targetClass.AddMembers(field, GetPropertyWithFieldRoslyn(fieldName, field)); - - PropertyDeclarationSyntax propertyDeclaration = GetPropertyRoslyn(fieldName, SyntaxFactory.ParseTypeName(fullItem.ToString())); - propertyDeclaration = propertyDeclaration.WithLeadingTrivia(GetCommentsRoslyn(typeField.Docs, null, fieldName)); - - targetClass = targetClass.AddMembers(propertyDeclaration); - } - } - - 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); - - TargetUnit = TargetUnit.AddMembers(compilationUnit.Members.ToArray()); - - return this; - } - - private static string GetFieldName(NodeTypeField typeField, string alterName, int length, int index) - { - if (typeField.Name == null) - { - if (length > 1) - { - if (typeField.TypeName == null) - { - return alterName + index; - } - else - { - return typeField.TypeName; - } - } - else - { - return alterName; - } - } - else - { - return typeField.Name; - } - } - } -} \ No newline at end of file From 5aa461d81d29f40c4c90e84dfff9902c251a696f Mon Sep 17 00:00:00 2001 From: "cedric.decoster" Date: Tue, 30 May 2023 22:04:22 +0200 Subject: [PATCH 13/14] removed old codedom --- .../Service/Node/Old/ArrayBuilderCodeDom.cs | 184 ------ .../Service/Node/Old/BuilderBaseCodeDom.cs | 180 ------ .../Node/Old/ClientBuilderBaseCodeDom.cs | 18 - .../Service/Node/Old/ClientBuilderCodeDom.cs | 114 ---- .../Service/Node/Old/EnumBuilderCodeDom.cs | 280 --------- .../Node/Old/ModuleBuilderBaseCodeDom.cs | 25 - .../Node/Old/ModuleGenBuilderCodeDom.cs | 535 ------------------ ...stServiceControllerModuleBuilderCodeDom.cs | 195 ------- .../RestServiceStorageModuleBuilderCodeDom.cs | 253 --------- .../Service/Node/Old/StructBuilderCodeDom.cs | 198 ------- .../Node/Old/TypeBuilderBaseCodeDom.cs | 16 - 11 files changed, 1998 deletions(-) delete mode 100644 Tools/Substrate.DotNet/Service/Node/Old/ArrayBuilderCodeDom.cs delete mode 100644 Tools/Substrate.DotNet/Service/Node/Old/BuilderBaseCodeDom.cs delete mode 100644 Tools/Substrate.DotNet/Service/Node/Old/ClientBuilderBaseCodeDom.cs delete mode 100644 Tools/Substrate.DotNet/Service/Node/Old/ClientBuilderCodeDom.cs delete mode 100644 Tools/Substrate.DotNet/Service/Node/Old/EnumBuilderCodeDom.cs delete mode 100644 Tools/Substrate.DotNet/Service/Node/Old/ModuleBuilderBaseCodeDom.cs delete mode 100644 Tools/Substrate.DotNet/Service/Node/Old/ModuleGenBuilderCodeDom.cs delete mode 100644 Tools/Substrate.DotNet/Service/Node/Old/RestServiceControllerModuleBuilderCodeDom.cs delete mode 100644 Tools/Substrate.DotNet/Service/Node/Old/RestServiceStorageModuleBuilderCodeDom.cs delete mode 100644 Tools/Substrate.DotNet/Service/Node/Old/StructBuilderCodeDom.cs delete mode 100644 Tools/Substrate.DotNet/Service/Node/Old/TypeBuilderBaseCodeDom.cs diff --git a/Tools/Substrate.DotNet/Service/Node/Old/ArrayBuilderCodeDom.cs b/Tools/Substrate.DotNet/Service/Node/Old/ArrayBuilderCodeDom.cs deleted file mode 100644 index a6c89b5..0000000 --- a/Tools/Substrate.DotNet/Service/Node/Old/ArrayBuilderCodeDom.cs +++ /dev/null @@ -1,184 +0,0 @@ -using Substrate.DotNet.Service.Node.Base; -using Substrate.NetApi.Model.Meta; -using System; -using System.CodeDom; -using System.Linq; -using System.Reflection; - -namespace Substrate.DotNet.Service.Node -{ - public class ArrayBuilderCodeDom : TypeBuilderBaseCodeDom - { - public static int Counter = 0; - - private ArrayBuilderCodeDom(string projectName, uint id, NodeTypeArray typeDef, NodeTypeResolver typeDict) - : base(projectName, id, typeDef, typeDict) - { - } - - private static CodeMemberMethod GetDecode(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++) " + - "{" + - $"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")); - return decodeMethod; - } - - private static CodeMemberMethod GetEncode() - { - 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()")); - return encodeMethod; - } - - public static ArrayBuilderCodeDom Create(string projectName, uint id, NodeTypeArray nodeType, NodeTypeResolver typeDict) - { - return new ArrayBuilderCodeDom(projectName, id, nodeType, typeDict); - } - - public override TypeBuilderBaseCodeDom Create() - { - var typeDef = TypeDef as NodeTypeArray; - - NodeTypeResolved fullItem = GetFullItemPath(typeDef.TypeId); - - ClassName = $"Arr{typeDef.Length}{fullItem.ClassName}"; - - CodeNamespace typeNamespace = new(NamespaceName); - TargetUnit.Namespaces.Add(typeNamespace); - - if (ClassName.Any(ch => !char.IsLetterOrDigit(ch))) - { - 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")); - - // add comment to class if exists - targetClass.Comments.AddRange(GetComments(typeDef.Docs, typeDef)); - AddTargetClassCustomAttributes(targetClass, typeDef); - - typeNamespace.Types.Add(targetClass); - - // 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); - - CodeMemberMethod encodeMethod = ArrayBuilderCodeDom.GetEncode(); - targetClass.Members.Add(encodeMethod); - - CodeMemberMethod decodeMethod = ArrayBuilderCodeDom.GetDecode(fullItem.ToString()); - targetClass.Members.Add(decodeMethod); - - 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/Old/BuilderBaseCodeDom.cs b/Tools/Substrate.DotNet/Service/Node/Old/BuilderBaseCodeDom.cs deleted file mode 100644 index 6c3332f..0000000 --- a/Tools/Substrate.DotNet/Service/Node/Old/BuilderBaseCodeDom.cs +++ /dev/null @@ -1,180 +0,0 @@ -using Substrate.NetApi.Model.Meta; -using System.CodeDom; -using System.CodeDom.Compiler; -using System.Collections.Generic; -using System.IO; -using System.Linq; - -namespace Substrate.DotNet.Service.Node.Base -{ - public abstract class BuilderBaseCodeDom - { - public static readonly List Files = new(); - - public uint Id { get; } - - private NodeTypeResolver Resolver { get; } - - public bool Success { get; set; } - - public string NamespaceName { get; protected set; } - - internal string FileName { get; set; } - - public string ClassName { get; set; } - - public string ReferenzName { get; set; } - - public string ProjectName { get; private set; } - - public CodeNamespace ImportsNamespace { get; set; } - - public CodeCompileUnit TargetUnit { get; set; } - - public abstract BuilderBaseCodeDom Create(); - - public BuilderBaseCodeDom(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); - - Success = true; - } - - public NodeTypeResolved GetFullItemPath(uint typeId) - { - 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) - { - 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 (docs != null) - { - foreach (string doc in docs) - { - comments.Add(new CodeCommentStatement(doc, true)); - } - } - - comments.Add(new CodeCommentStatement("", true)); - return comments; - } - - public static CodeMemberMethod SimpleMethod(string name, string returnType = null, object returnExpression = null) - { - CodeMemberMethod nameMethod = new() - { - Attributes = MemberAttributes.Public | MemberAttributes.Override, - Name = name - }; - - if (returnType != null) - { - nameMethod.ReturnType = new CodeTypeReference(returnType); - CodeMethodReturnStatement nameReturnStatement = new() - { - Expression = new CodePrimitiveExpression(returnExpression) - }; - nameMethod.Statements.Add(nameReturnStatement); - } - - return nameMethod; - } - - 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)) - { - // TODO (svnscha) Why does this happen? - // Console.WriteLine($"Overwriting[BUG]: {path}"); - //path += _index++; - } - else - { - Files.Add(path); - } - - using StreamWriter sourceWriter = new(path); - provider.GenerateCodeFromCompileUnit( - TargetUnit, sourceWriter, options); - } - } - - private string GetPath(string basePath) - { - var space = NamespaceName.Split('.').ToList(); - - 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. - space = space.TakeLast(space.Count - 2).ToList(); - - // Add base path at the beginning of the paths list - if (!string.IsNullOrEmpty(basePath)) - { - space.Insert(0, basePath); - } - - string path = Path.Combine(space.ToArray()); - - return path; - } - - protected void AddTargetClassCustomAttributes(CodeTypeDeclaration 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")); - - targetClass.CustomAttributes.Add(new CodeAttributeDeclaration(new CodeTypeReference("SubstrateNodeType"), new CodeAttributeArgument( - new CodeSnippetExpression($"TypeDefEnum.{typeDef.TypeDef}") - ))); - } - } -} \ No newline at end of file diff --git a/Tools/Substrate.DotNet/Service/Node/Old/ClientBuilderBaseCodeDom.cs b/Tools/Substrate.DotNet/Service/Node/Old/ClientBuilderBaseCodeDom.cs deleted file mode 100644 index ae3143f..0000000 --- a/Tools/Substrate.DotNet/Service/Node/Old/ClientBuilderBaseCodeDom.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.CodeDom; -using System.Collections.Generic; - -namespace Substrate.DotNet.Service.Node.Base -{ - public abstract class ClientBuilderBaseCodeDom : BuilderBaseCodeDom - { - public List ModuleNames { get; } - - public ClientBuilderBaseCodeDom(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")); - } - } -} \ No newline at end of file diff --git a/Tools/Substrate.DotNet/Service/Node/Old/ClientBuilderCodeDom.cs b/Tools/Substrate.DotNet/Service/Node/Old/ClientBuilderCodeDom.cs deleted file mode 100644 index 9a00d3c..0000000 --- a/Tools/Substrate.DotNet/Service/Node/Old/ClientBuilderCodeDom.cs +++ /dev/null @@ -1,114 +0,0 @@ -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 System.Collections.Generic; -using System.Reflection; - -namespace Substrate.DotNet.Service.Node -{ - public class ClientBuilderCodeDom : ClientBuilderBaseCodeDom - { - private ClientBuilderCodeDom(string projectName, uint id, List moduleNames, NodeTypeResolver typeDict) : - base(projectName, id, moduleNames, typeDict) - { - } - - public static ClientBuilderCodeDom Init(string projectName, uint id, List moduleNames, NodeTypeResolver typeDict) - { - return new ClientBuilderCodeDom(projectName, id, moduleNames, typeDict); - } - - public override ClientBuilderCodeDom 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[] { }))); - - 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)); - } - } - - return this; - } - } -} \ No newline at end of file diff --git a/Tools/Substrate.DotNet/Service/Node/Old/EnumBuilderCodeDom.cs b/Tools/Substrate.DotNet/Service/Node/Old/EnumBuilderCodeDom.cs deleted file mode 100644 index 492aaea..0000000 --- a/Tools/Substrate.DotNet/Service/Node/Old/EnumBuilderCodeDom.cs +++ /dev/null @@ -1,280 +0,0 @@ -using Substrate.DotNet.Service.Node.Base; -using Substrate.NetApi.Model.Meta; -using Substrate.NetApi.Model.Types; -using System; -using System.CodeDom; -using System.Linq; -using System.Reflection; - -namespace Substrate.DotNet.Service.Node -{ - public class EnumBuilderCodeDom : TypeBuilderBaseCodeDom - { - private EnumBuilderCodeDom(string projectName, uint id, NodeTypeVariant typeDef, NodeTypeResolver typeDict) - : base(projectName, id, typeDef, typeDict) - { - } - - public static EnumBuilderCodeDom Init(string projectName, uint id, NodeTypeVariant typeDef, NodeTypeResolver typeDict) - { - return new EnumBuilderCodeDom(projectName, id, typeDef, typeDict); - } - - public override TypeBuilderBaseCodeDom Create() - { - var typeDef = TypeDef as NodeTypeVariant; - - string enumName = $"{typeDef.Path.Last()}"; - - 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 (TypeVariant variant in typeDef.Variants) - { - TargetType.Members.Add(new CodeMemberField(ClassName, variant.Name) - { - InitExpression = new CodePrimitiveExpression(variant.Index) - }); - } - } - typeNamespace.Types.Add(TargetType); - - var targetClass = new CodeTypeDeclaration(ClassName) - { - IsClass = true, - TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed - }; - targetClass.Comments.AddRange(GetComments(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); - } - else - { - var codeTypeRef = new CodeTypeReference("BaseEnumExt"); - codeTypeRef.TypeArguments.Add(new CodeTypeReference(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]; - if (variant == null || variant.TypeFields == null) - { - // add void type - codeTypeRef.TypeArguments.Add(new CodeTypeReference("BaseVoid")); - } - else - { - if (variant.TypeFields.Length == 1) - { - NodeTypeResolved item = GetFullItemPath(variant.TypeFields[0].TypeId); - codeTypeRef.TypeArguments.Add(new CodeTypeReference(item.ToString())); - } - else - { - var baseTuple = new CodeTypeReference("BaseTuple"); - - foreach (NodeTypeField field in variant.TypeFields) - { - NodeTypeResolved item = GetFullItemPath(field.TypeId); - baseTuple.TypeArguments.Add(new CodeTypeReference(item.ToString())); - } - codeTypeRef.TypeArguments.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."); - } - } - - targetClass.BaseTypes.Add(codeTypeRef); - typeNamespace.Types.Add(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; - } - - 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; - } - } -} \ No newline at end of file diff --git a/Tools/Substrate.DotNet/Service/Node/Old/ModuleBuilderBaseCodeDom.cs b/Tools/Substrate.DotNet/Service/Node/Old/ModuleBuilderBaseCodeDom.cs deleted file mode 100644 index 2f89537..0000000 --- a/Tools/Substrate.DotNet/Service/Node/Old/ModuleBuilderBaseCodeDom.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Substrate.DotNet.Extensions; -using Substrate.NetApi.Model.Meta; -using System.Collections.Generic; - -namespace Substrate.DotNet.Service.Node.Base -{ - public abstract class ModuleBuilderBaseCodeDom : BuilderBaseCodeDom - { - public Dictionary NodeTypes { get; private set; } - - public PalletModule Module { get; private set; } - - public string PrefixName { get; private set; } - - public ModuleBuilderBaseCodeDom(string projectName, uint id, PalletModule module, NodeTypeResolver typeDict, - Dictionary nodeTypes) - : base(projectName, id, typeDict) - { - NodeTypes = nodeTypes; - Module = module; - PrefixName = module.Name == "System" ? "Frame" : "Pallet"; - NamespaceName = $"{ProjectName}.Generated.Model.{PrefixName + module.Name.MakeMethod()}"; - } - } -} \ No newline at end of file diff --git a/Tools/Substrate.DotNet/Service/Node/Old/ModuleGenBuilderCodeDom.cs b/Tools/Substrate.DotNet/Service/Node/Old/ModuleGenBuilderCodeDom.cs deleted file mode 100644 index ed54503..0000000 --- a/Tools/Substrate.DotNet/Service/Node/Old/ModuleGenBuilderCodeDom.cs +++ /dev/null @@ -1,535 +0,0 @@ -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 -{ - public class ModuleGenBuilderCodeDom : ModuleBuilderBaseCodeDom - { - private ModuleGenBuilderCodeDom(string projectName, uint id, PalletModule module, NodeTypeResolver typeDict, Dictionary nodeTypes) : - base(projectName, id, module, typeDict, nodeTypes) - { - } - - public static ModuleGenBuilderCodeDom Init(string projectName, uint id, PalletModule module, NodeTypeResolver typeDict, Dictionary nodeTypes) - { - return new ModuleGenBuilderCodeDom(projectName, id, module, typeDict, nodeTypes); - } - - public override ModuleGenBuilderCodeDom 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")); - - FileName = "Main" + Module.Name; - NamespaceName = $"{ProjectName}.Generated.Storage"; - ReferenzName = NamespaceName; - - CodeNamespace typeNamespace = new(NamespaceName); - TargetUnit.Namespaces.Add(typeNamespace); - - // add constructor - CodeConstructor constructor = new() - { - Attributes = MemberAttributes.Public | MemberAttributes.Final - }; - - CreateStorage(typeNamespace, constructor); - CreateCalls(typeNamespace); - CreateEvents(typeNamespace); - CreateConstants(typeNamespace); - CreateErrors(typeNamespace); - - return this; - } - - private void CreateStorage(CodeNamespace typeNamespace, CodeConstructor constructor) - { - 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); - - // 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); - - 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)); - - targetClass.Members.Add(storageMethod); - - if (entry.StorageType == Storage.Type.Plain) - { - NodeTypeResolved fullItem = GetFullItemPath(entry.TypeMap.Item1); - - parameterMethod.Statements.Add(new CodeMethodReturnStatement( - GetStorageString(storage.Prefix, entry.Name, entry.StorageType))); - - storageMethod.ReturnType = new CodeTypeReference($"async Task<{fullItem}>"); - - storageMethod.Parameters.Add(new CodeParameterDeclarationExpression("CancellationToken", "token")); - - CodeMethodInvokeExpression methodInvoke = new( - new CodeTypeReferenceExpression(targetClass.Name), - parameterMethod.Name, Array.Empty()); - - CodeVariableDeclarationStatement variableDeclaration1 = new(typeof(string), "parameters", methodInvoke); - storageMethod.Statements.Add(variableDeclaration1); - - // create result - var resultStatement = new CodeArgumentReferenceExpression(GetInvoceString(fullItem.ToString())); - CodeVariableDeclarationStatement variableDeclaration2 = new("var", "result", resultStatement); - storageMethod.Statements.Add(variableDeclaration2); - - // return statement - storageMethod.Statements.Add( - new CodeMethodReturnStatement( - new CodeVariableReferenceExpression("result"))); - - // add storage key mapping in constructor - constructor.Statements.Add( - AddPropertyValues(GetStorageMapString("", fullItem.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( - 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 CodeArgumentReferenceExpression("key")); - CodeVariableDeclarationStatement variableDeclaration = new(typeof(string), "parameters", methodInvoke); - storageMethod.Statements.Add(variableDeclaration); - - // create result - var resultStatement = new CodeArgumentReferenceExpression(GetInvoceString(value.ToString())); - CodeVariableDeclarationStatement variableDeclaration2 = new("var", "result", resultStatement); - storageMethod.Statements.Add(variableDeclaration2); - - - // return statement - storageMethod.Statements.Add( - new CodeMethodReturnStatement( - new CodeVariableReferenceExpression("result"))); - - // add storage key mapping in constructor - constructor.Statements.Add(ModuleGenBuilderCodeDom.AddPropertyValues(ModuleGenBuilderCodeDom.GetStorageMapString(key.ToString(), value.ToString(), storage.Prefix, entry.Name, hashers), "_client.StorageKeyDict")); - } - else - { - throw new NotImplementedException(); - } - } - } - } - - private void CreateCalls(CodeNamespace typeNamespace) - { - ClassName = Module.Name + "Calls"; - - PalletCalls calls = Module.Calls; - - var targetClass = new CodeTypeDeclaration(ClassName) - { - IsClass = true, - TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed - }; - typeNamespace.Types.Add(targetClass); - - if (calls != null) - { - if (NodeTypes.TryGetValue(calls.TypeId, out NodeType nodeType)) - { - var typeDef = nodeType as NodeTypeVariant; - - if (typeDef.Variants != null) - { - 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) - }; - - // add comment to class if exists - callMethod.Comments.AddRange(GetComments(typeDef.Docs, null, variant.Name)); - - string byteArrayName = "byteArray"; - - callMethod.Statements.Add(new CodeVariableDeclarationStatement( - typeof(List), byteArrayName, new CodeObjectCreateExpression("List", Array.Empty()))); - - if (variant.TypeFields != null) - { - foreach (NodeTypeField field in variant.TypeFields) - { - 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"))); - } - } - - // 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); - } - } - } - } - } - - private void CreateEvents(CodeNamespace typeNamespace) - { - ClassName = Module.Name + "Events"; - - PalletEvents events = Module.Events; - - //if (events != null) - //{ - // if (NodeTypes.TryGetValue(events.TypeId, out NodeType nodeType)) - // { - // var typeDef = nodeType as NodeTypeVariant; - - // if (typeDef.Variants != null) - // { - // 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 - // // 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); - // } - // } - // } - //} - } - - private void CreateConstants(CodeNamespace typeNamespace) - { - ClassName = Module.Name + "Constants"; - - PalletConstant[] constants = Module.Constants; - - var targetClass = new CodeTypeDeclaration(ClassName) - { - IsClass = true, - TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed - }; - typeNamespace.Types.Add(targetClass); - - if (constants != null && constants.Any()) - { - foreach (PalletConstant constant in constants) - { - // async Task - CodeMemberMethod constantMethod = new() - { - Attributes = MemberAttributes.Public | MemberAttributes.Final, - Name = constant.Name, - }; - // add comment to class if exists - constantMethod.Comments.AddRange(GetComments(constant.Docs, null, constant.Name)); - - targetClass.Members.Add(constantMethod); - - if (NodeTypes.TryGetValue(constant.TypeId, out NodeType nodeType)) - { - NodeTypeResolved nodeTypeResolved = GetFullItemPath(nodeType.Id); - constantMethod.ReturnType = new CodeTypeReference(nodeTypeResolved.ToString()); - - // assign new result object - CodeVariableDeclarationStatement newStatement = new("var", "result", new CodeObjectCreateExpression(nodeTypeResolved.ToString(), Array.Empty())); - constantMethod.Statements.Add(newStatement); - - // 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); - - // return statement - constantMethod.Statements.Add( - new CodeMethodReturnStatement( - new CodeVariableReferenceExpression("result"))); - } - } - } - } - - private void CreateErrors(CodeNamespace typeNamespace) - { - ClassName = Module.Name + "Errors"; - - PalletErrors errors = Module.Errors; - - if (errors != null) - { - if (NodeTypes.TryGetValue(errors.TypeId, out NodeType nodeType)) - { - var typeDef = nodeType as NodeTypeVariant; - - var targetClass = new CodeTypeDeclaration(ClassName) - { - IsEnum = true, - TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed - }; - - if (typeDef.Variants != null) - { - foreach (TypeVariant variant in typeDef.Variants) - { - var enumField = new CodeMemberField(ClassName, variant.Name); - - // add comment to field if exists - enumField.Comments.AddRange(GetComments(variant.Docs, null, variant.Name)); - - targetClass.Members.Add(enumField); - } - } - - typeNamespace.Types.Add(targetClass); - } - } - } - - 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) - { - var codeExpressions = - new CodeExpression[] { - new CodePrimitiveExpression(module), - new CodePrimitiveExpression(item), - new CodePropertyReferenceExpression(new CodeTypeReferenceExpression(typeof(Storage.Type)), 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") - }); - - if (hashers.Length > 1) - { - keyReference = new CodeSnippetExpression("key.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 - }; - } - - return new CodeMethodInvokeExpression(new CodeTypeReferenceExpression("RequestGenerator"), "GetStorage", codeExpressions); - } - - private static CodeExpression[] GetStorageMapString(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}) - }; - - // 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 - }) - }; - } - - return result; - } - - private static CodeStatement AddPropertyValues(CodeExpression[] exprs, string variableReference) - { - return new CodeExpressionStatement( - new CodeMethodInvokeExpression( - new CodeMethodReferenceExpression( - new CodeTypeReferenceExpression( - new CodeTypeReference(variableReference)), "Add"), exprs)); - } - } -} \ No newline at end of file diff --git a/Tools/Substrate.DotNet/Service/Node/Old/RestServiceControllerModuleBuilderCodeDom.cs b/Tools/Substrate.DotNet/Service/Node/Old/RestServiceControllerModuleBuilderCodeDom.cs deleted file mode 100644 index 07174b1..0000000 --- a/Tools/Substrate.DotNet/Service/Node/Old/RestServiceControllerModuleBuilderCodeDom.cs +++ /dev/null @@ -1,195 +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 RestServiceControllerModuleBuilderCodeDom : ModuleBuilderBaseCodeDom - { - private string NetApiProjectName { get; } - - private RestServiceControllerModuleBuilderCodeDom(string projectName, string netApiProjectName, uint id, PalletModule module, NodeTypeResolver typeDict, Dictionary nodeTypes) : - base(projectName, id, module, typeDict, nodeTypes) - { - NetApiProjectName = netApiProjectName; - } - - public static RestServiceControllerModuleBuilderCodeDom Init(string projectName, string netApiProjectName, uint id, PalletModule module, NodeTypeResolver typeDict, Dictionary nodeTypes) - { - return new RestServiceControllerModuleBuilderCodeDom(projectName, netApiProjectName, id, module, typeDict, nodeTypes); - } - - public override RestServiceControllerModuleBuilderCodeDom 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); - - CreateController(typeNamespace); - - return this; - } - - private void CreateController(CodeNamespace typeNamespace) - { - ClassName = FileName; - - var targetClass = new CodeTypeDeclaration(ClassName) - { - IsClass = true, - TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed - }; - targetClass.BaseTypes.Add(new CodeTypeReference("ControllerBase")); - targetClass.Comments.AddRange(GetComments(new string[] { $"{ClassName} controller to access storages." })); - - typeNamespace.Types.Add(targetClass); - targetClass.CustomAttributes.Add( - new CodeAttributeDeclaration("ApiController")); - targetClass.CustomAttributes.Add( - new CodeAttributeDeclaration("Route", - new CodeAttributeArgument[] { - new CodeAttributeArgument(new CodePrimitiveExpression("[controller]")) - })); - - string fieldName = $"{Module.Storage.Prefix}Storage"; - CodeMemberField field = new() - { - Attributes = MemberAttributes.Private, - Name = fieldName.MakePrivateField(), - Type = new CodeTypeReference($"I{fieldName}") - }; - targetClass.Members.Add(field); - - CodeConstructor constructor = new() - { - Attributes = - MemberAttributes.Public | MemberAttributes.Final - }; - targetClass.Members.Add(constructor); - constructor.Comments.AddRange(GetComments(new string[] { $"{ClassName} constructor." })); - - constructor.Parameters.Add(new CodeParameterDeclarationExpression($"I{fieldName}", fieldName.MakePublicField())); - - // constructor initialize storage properties - constructor.Statements.Add(new CodeAssignStatement( - new CodeVariableReferenceExpression(field.Name), - new CodeVariableReferenceExpression(fieldName.MakePublicField()))); - - if (Module.Storage.Entries != null) - { - 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); - } - } - } - } -} \ No newline at end of file diff --git a/Tools/Substrate.DotNet/Service/Node/Old/RestServiceStorageModuleBuilderCodeDom.cs b/Tools/Substrate.DotNet/Service/Node/Old/RestServiceStorageModuleBuilderCodeDom.cs deleted file mode 100644 index adc033a..0000000 --- a/Tools/Substrate.DotNet/Service/Node/Old/RestServiceStorageModuleBuilderCodeDom.cs +++ /dev/null @@ -1,253 +0,0 @@ -using Substrate.DotNet.Extensions; -using Substrate.DotNet.Service.Node.Base; -using Substrate.NetApi.Model.Meta; -using Substrate.ServiceLayer.Storage; -using System; -using System.CodeDom; -using System.Collections.Generic; -using System.Reflection; - -namespace Substrate.DotNet.Service.Node -{ - public class RestServiceStorageModuleBuilderCodeDom : ModuleBuilderBaseCodeDom - { - private RestServiceStorageModuleBuilderCodeDom(string projectName, uint id, PalletModule module, NodeTypeResolver typeDict, Dictionary nodeTypes) : - base(projectName, id, module, typeDict, nodeTypes) - { - } - - public static RestServiceStorageModuleBuilderCodeDom Init(string projectName, uint id, PalletModule module, NodeTypeResolver typeDict, Dictionary nodeTypes) - { - return new RestServiceStorageModuleBuilderCodeDom(projectName, id, module, typeDict, nodeTypes); - } - - public override RestServiceStorageModuleBuilderCodeDom 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); - - CreateStorage(typeNamespace); - return this; - } - - private void CreateStorage(CodeNamespace typeNamespace) - { - 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)); - - typeNamespace.Types.Add(targetClass); - - CodeConstructor constructor = new() - { - 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." })); - - targetClass.Members.Add(constructor); - - CodeMemberMethod initializeAsyncMethod = new() - { - Attributes = MemberAttributes.Public | MemberAttributes.Final, - Name = $"InitializeAsync", - ReturnType = new CodeTypeReference("async Task") - }; - 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." })); - - if (Module.Storage.Entries != null) - { - var keyParamter = new CodeParameterDeclarationExpression(typeof(string), "key"); - var dataParamter = new CodeParameterDeclarationExpression(typeof(string), "data"); - - foreach (Entry entry in Module.Storage.Entries) - { - CodeTypeReference baseReturnType; - CodeTypeReference returnType; - CodeExpression[] 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()}>"); - - updateExpression = new CodeExpression[] { - new CodeVariableReferenceExpression(dataParamter.Name)}; - tryGetExpression = Array.Empty(); - } - 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); - 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 - } - }; - } - else - { - throw new NotImplementedException(); - } - - // create typed storage field - CodeMemberField field = new() - { - Attributes = MemberAttributes.Private, - Name = $"{entry.Name.MakePrivateField()}TypedStorage", - Type = returnType - }; - - field.Comments.AddRange(GetComments(new string[] { $"{field.Name} typed storage field" })); - targetClass.Members.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); - - // 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") - }))); - - // 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); - - // create on update - CodeMemberMethod onUpdateMethod = new() - { - Attributes = MemberAttributes.Public | MemberAttributes.Final, - Name = $"OnUpdate{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)); - - targetInterface.Members.Add(getStorageMethod); - - if (tryGetExpression.Length == 0) - { - onUpdateMethod.Parameters.Add(dataParamter); - - getStorageMethod.Statements.Add(new CodeMethodReturnStatement( - new CodeMethodInvokeExpression( - new CodeVariableReferenceExpression(prop.Name), - "Get", Array.Empty()))); - } - 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)) })); - } - - targetClass.Members.Add(getStorageMethod); - } - } - } - } -} \ No newline at end of file diff --git a/Tools/Substrate.DotNet/Service/Node/Old/StructBuilderCodeDom.cs b/Tools/Substrate.DotNet/Service/Node/Old/StructBuilderCodeDom.cs deleted file mode 100644 index bf521cf..0000000 --- a/Tools/Substrate.DotNet/Service/Node/Old/StructBuilderCodeDom.cs +++ /dev/null @@ -1,198 +0,0 @@ -using Substrate.DotNet.Extensions; -using Substrate.DotNet.Service.Node.Base; -using Substrate.NetApi.Model.Meta; -using System.CodeDom; -using System.Linq; -using System.Reflection; - -namespace Substrate.DotNet.Service.Node -{ - public class StructBuilderCodeDom : TypeBuilderBaseCodeDom - { - private StructBuilderCodeDom(string projectName, uint id, NodeTypeComposite typeDef, NodeTypeResolver typeDict) - : base(projectName, id, typeDef, typeDict) - { - } - - private static CodeMemberField GetPropertyField(string name, string baseType) - { - CodeMemberField field = new() - { - Attributes = MemberAttributes.Private, - Name = name.MakePrivateField(), - Type = new CodeTypeReference($"{baseType}") - }; - return field; - } - - private static CodeMemberProperty GetProperty(string name, CodeMemberField 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())); - return prop; - } - - private CodeMemberMethod GetDecode(NodeTypeField[] typeFields) - { - 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")); - - if (typeFields != null) - { - for (int i = 0; i < typeFields.Length; i++) - { - NodeTypeField typeField = typeFields[i]; - - string fieldName = StructBuilderCodeDom.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.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)")); - - return decodeMethod; - } - - private static CodeMemberMethod GetEncode(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()")); - - if (typeFields != null) - { - for (int i = 0; i < typeFields.Length; i++) - { - NodeTypeField typeField = typeFields[i]; - string fieldName = StructBuilderCodeDom.GetFieldName(typeField, "value", typeFields.Length, i); - - encodeMethod.Statements.Add(new CodeSnippetExpression($"result.AddRange({fieldName.MakeMethod()}.Encode())")); - } - } - - encodeMethod.Statements.Add(new CodeSnippetExpression("return result.ToArray()")); - return encodeMethod; - } - - public static BuilderBaseCodeDom Init(string projectName, uint id, NodeTypeComposite typeDef, NodeTypeResolver typeDict) - { - return new StructBuilderCodeDom(projectName, id, typeDef, typeDict); - } - - public override TypeBuilderBaseCodeDom Create() - { - var typeDef = TypeDef as NodeTypeComposite; - - ClassName = $"{typeDef.Path.Last()}"; - - ReferenzName = $"{NamespaceName}.{typeDef.Path.Last()}"; - - 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")); - - // 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); - - if (typeDef.TypeFields != null) - { - for (int i = 0; i < typeDef.TypeFields.Length; i++) - { - NodeTypeField typeField = typeDef.TypeFields[i]; - string fieldName = StructBuilderCodeDom.GetFieldName(typeField, "value", typeDef.TypeFields.Length, i); - - NodeTypeResolved fullItem = GetFullItemPath(typeField.TypeId); - - CodeMemberField field = StructBuilderCodeDom.GetPropertyField(fieldName, fullItem.ToString()); - - // add comment to field if exists - field.Comments.AddRange(GetComments(typeField.Docs, null, fieldName)); - - targetClass.Members.Add(field); - targetClass.Members.Add(StructBuilderCodeDom.GetProperty(fieldName, field)); - } - } - - CodeMemberMethod encodeMethod = StructBuilderCodeDom.GetEncode(typeDef.TypeFields); - targetClass.Members.Add(encodeMethod); - - CodeMemberMethod decodeMethod = GetDecode(typeDef.TypeFields); - targetClass.Members.Add(decodeMethod); - - return this; - } - - private static string GetFieldName(NodeTypeField typeField, string alterName, int length, int index) - { - if (typeField.Name == null) - { - if (length > 1) - { - if (typeField.TypeName == null) - { - return alterName + index; - } - else - { - return typeField.TypeName; - } - } - else - { - return alterName; - } - } - else - { - return typeField.Name; - } - } - } -} \ No newline at end of file diff --git a/Tools/Substrate.DotNet/Service/Node/Old/TypeBuilderBaseCodeDom.cs b/Tools/Substrate.DotNet/Service/Node/Old/TypeBuilderBaseCodeDom.cs deleted file mode 100644 index 78939ce..0000000 --- a/Tools/Substrate.DotNet/Service/Node/Old/TypeBuilderBaseCodeDom.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Substrate.NetApi.Model.Meta; - -namespace Substrate.DotNet.Service.Node.Base -{ - public abstract class TypeBuilderBaseCodeDom : BuilderBaseCodeDom - { - public NodeType TypeDef { get; } - - public TypeBuilderBaseCodeDom(string projectName, uint id, NodeType typeDef, NodeTypeResolver resolver) - : base(projectName, id, resolver) - { - TypeDef = typeDef; - NamespaceName = resolver.GetNamespace(id); - } - } -} \ No newline at end of file From 507fe5b9f43f1571df068ed3ab8bd89e59e68266 Mon Sep 17 00:00:00 2001 From: Apolixit Date: Sat, 12 Aug 2023 20:27:47 +0200 Subject: [PATCH 14/14] Add TypeSize property to ArrayBuilder --- Tools/Substrate.DotNet/Service/Node/ArrayBuilder.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Tools/Substrate.DotNet/Service/Node/ArrayBuilder.cs b/Tools/Substrate.DotNet/Service/Node/ArrayBuilder.cs index d071e39..e4440d8 100644 --- a/Tools/Substrate.DotNet/Service/Node/ArrayBuilder.cs +++ b/Tools/Substrate.DotNet/Service/Node/ArrayBuilder.cs @@ -108,6 +108,18 @@ public override TypeBuilderBase Create() 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(