diff --git a/src/ContractGenerator/ContractGenerator.cs b/src/ContractGenerator/ContractGenerator.cs
index ab44cf5..210d509 100644
--- a/src/ContractGenerator/ContractGenerator.cs
+++ b/src/ContractGenerator/ContractGenerator.cs
@@ -31,7 +31,7 @@ private static string GetServicesFilename(FileDescriptor fileDescriptor)
/// Generates a set of C# files from the input stream containing the proto source. This is the primary entry-point into
/// the ContractPlugin.
///
- public CodeGeneratorResponse Generate(Stream stdin)
+ public static CodeGeneratorResponse Generate(Stream stdin)
{
throw new NotImplementedException();
}
diff --git a/src/ContractGenerator/ProtoUtils.cs b/src/ContractGenerator/ProtoUtils.cs
index 4a4ca44..87720c0 100644
--- a/src/ContractGenerator/ProtoUtils.cs
+++ b/src/ContractGenerator/ProtoUtils.cs
@@ -1,3 +1,4 @@
+using System.Text;
using Google.Protobuf.Reflection;
namespace ContractGenerator;
@@ -16,6 +17,84 @@ public static string GetAccessLevel(byte flags)
return (flags & FlagConstants.InternalAccess) != 0 ? "internal" : "public";
}
+ ///
+ /// This Util GetCsharpComments gets/generates C# comments based on the proto. Copied from the C++ original
+ /// https://github.com/AElfProject/contract-plugin/blob/de625fcb79f83603e29d201c8488f101b40f573c/src/contract_csharp_generator_helpers.h#L37
+ ///
+ public static string GetCsharpComments(IDescriptor desc, bool leading)
+ {
+ return GetPrefixedComments(desc, leading, "//");
+ }
+
+ ///
+ /// This Util gets the GetPrefixedComments based on the proto. Copied from the C++ original
+ /// https://github.com/AElfProject/contract-plugin/blob/de625fcb79f83603e29d201c8488f101b40f573c/src/generator_helpers.h#L257
+ ///
+ private static string GetPrefixedComments(IDescriptor desc, bool leading, string prefix)
+ {
+ var outComments = new List();
+
+ if (leading)
+ {
+ GetComment(desc, CommentType.LeadingDetached, outComments);
+ var leadingComments = new List();
+ GetComment(desc, CommentType.Leading, leadingComments);
+ outComments.AddRange(leadingComments);
+ }
+ else
+ {
+ GetComment(desc, CommentType.Trailing, outComments);
+ }
+
+ return GenerateCommentsWithPrefix(outComments, prefix);
+ }
+
+ private static string GenerateCommentsWithPrefix(IEnumerable input, string prefix)
+ {
+ var sb = new StringBuilder();
+ foreach (var elem in input.Where(elem => !string.IsNullOrEmpty(elem)))
+ if (elem != null && elem[0] == ' ')
+ sb.Append(prefix).Append(elem).Append("\n");
+ else
+ sb.Append(prefix).Append(" ").Append(elem).Append("\n");
+
+ return sb.ToString();
+ }
+
+ private static void GetComment(IDescriptor desc, CommentType type, ICollection outComments)
+ {
+ if (desc.File.ToProto().SourceCodeInfo == null) return;
+
+ var locations = desc.File.ToProto().SourceCodeInfo.Location;
+
+ foreach (var location in locations)
+ switch (type)
+ {
+ case CommentType.Leading:
+ case CommentType.Trailing:
+ {
+ var comments = type == CommentType.Leading ? location.LeadingComments : location.TrailingComments;
+ Split(comments, '\n', outComments);
+ break;
+ }
+ case CommentType.LeadingDetached:
+ {
+ foreach (var detachedComment in location.LeadingDetachedComments)
+ Split(detachedComment, '\n', outComments);
+
+ break;
+ }
+ default:
+ throw new Exception("Unknown comment type " + type);
+ }
+ }
+
+ private static void Split(string input, char delim, ICollection appendTo)
+ {
+ var substrings = input.Split(delim);
+ foreach (var substring in substrings) appendTo.Add(substring);
+ }
+
private static string ToCSharpName(string name, FileDescriptor fileDescriptor)
{
var result = GetFileNamespace(fileDescriptor);
@@ -157,4 +236,11 @@ internal static string UnderscoresToCamelCase(string input, bool capNextLetter,
result = '_' + result;
return result;
}
+
+ private enum CommentType
+ {
+ Leading,
+ Trailing,
+ LeadingDetached
+ }
}
diff --git a/test/ContractGenerator.Tests/ProtoUtilsTests.cs b/test/ContractGenerator.Tests/ProtoUtilsTests.cs
index 5303171..e5bcaaf 100644
--- a/test/ContractGenerator.Tests/ProtoUtilsTests.cs
+++ b/test/ContractGenerator.Tests/ProtoUtilsTests.cs
@@ -67,6 +67,32 @@ public void GetClassName_ReturnsCorrectClassName()
Assert.Equal("global::AElf.Contracts.HelloWorld.HelloWorld", className);
}
+ [Fact]
+ public void GetCsharpComments_ReturnsComments()
+ {
+ // Arrange: Create a DescriptorBase with a known FullName and File
+ var fds = GetFileDescriptorSet("helloworld");
+ var byteStrings = fds.File.Select(f => f.ToByteString());
+ var fileDescriptors = FileDescriptor.BuildFromByteStrings(byteStrings, _extensionRegistry);
+ var file = fileDescriptors[^1];
+
+ // Act: Call the GetClassName method
+ var comments = ProtoUtils.GetCsharpComments(file, true);
+ const string expectedComments = @"// These are test header comments!
+// The namespace of this class
+// The name of the state class the smart contract is going to use to access blockchain state
+// Actions (methods that modify contract state)
+// Stores the value in contract state
+// Views (methods that don't modify contract state)
+// Get the value stored from contract state
+// An event that will be emitted from contract method call
+";
+
+
+ // Assert: Verify the expected result
+ Assert.Equal(expectedComments, comments);
+ }
+
[Fact]
public void GetPropertyName_ReturnsCorrectPropertyName()
{
diff --git a/test/ContractGenerator.Tests/scripts/generate_descriptor.py b/test/ContractGenerator.Tests/scripts/generate_descriptor.py
index 31d7197..fb03f74 100644
--- a/test/ContractGenerator.Tests/scripts/generate_descriptor.py
+++ b/test/ContractGenerator.Tests/scripts/generate_descriptor.py
@@ -15,6 +15,7 @@ def get_command(testcase_name):
f'-o"{testcases_dir}/{testcase_name}/{descriptor_filename}"',
"--include_imports",
"--retain_options",
+ "--include_source_info",
proto_filename
]
diff --git a/test/ContractGenerator.Tests/testcases/helloworld/contract.proto b/test/ContractGenerator.Tests/testcases/helloworld/contract.proto
index 76ca41d..56d2259 100644
--- a/test/ContractGenerator.Tests/testcases/helloworld/contract.proto
+++ b/test/ContractGenerator.Tests/testcases/helloworld/contract.proto
@@ -1,5 +1,5 @@
syntax = "proto3";
-
+// These are test header comments!
import "aelf/options.proto";
import "google/protobuf/empty.proto";
import "google/protobuf/wrappers.proto";