This is an automated email from the ASF dual-hosted git repository.
chaokunyang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fory.git
The following commit(s) were added to refs/heads/main by this push:
new 7dbaabcea fix(rust): apply consistent pascalcase naming for nested
types (#3548)
7dbaabcea is described below
commit 7dbaabcea0c0aa89ec54274b3f36b4205b9b03c6
Author: Uğur Tafralı <[email protected]>
AuthorDate: Sun Apr 12 14:02:47 2026 +0300
fix(rust): apply consistent pascalcase naming for nested types (#3548)
## Why?
Rust generator was creating naming conflicts for nested types where
module paths and struct names could collide and cause compilation
errors.
## What does this PR do?
Applies consistent PascalCase naming to type names when resolving nested
type references and building struct definitions. Added tests for nested
type scenarios.
## Related issues
Fixes #3547
## AI Contribution Checklist (if applicable)
N/A
## Does this PR introduce any user-facing change?
Yes, code that previously failed to compile due to naming conflicts will
now generate valid Rust code.
## Benchmark (if perf-related)
N/A
---
compiler/fory_compiler/generators/rust.py | 18 +++---
compiler/fory_compiler/tests/test_nested_types.py | 76 +++++++++++++++++++++++
2 files changed, 85 insertions(+), 9 deletions(-)
diff --git a/compiler/fory_compiler/generators/rust.py
b/compiler/fory_compiler/generators/rust.py
index 48fad1f9b..75752face 100644
--- a/compiler/fory_compiler/generators/rust.py
+++ b/compiler/fory_compiler/generators/rust.py
@@ -172,9 +172,9 @@ class RustGenerator(BaseGenerator):
if "." in type_name:
parts = type_name.split(".")
parents = [self.to_snake_case(name) for name in parts[:-1]]
- path = "::".join(parents + [parts[-1]])
+ path = "::".join(parents + [self.to_pascal_case(parts[-1])])
return f"crate::{module}::{path}"
- return f"crate::{module}::{type_name}"
+ return f"crate::{module}::{self.to_pascal_case(type_name)}"
def generate_bytes_impl(self, type_name: str) -> List[str]:
lines = []
@@ -413,7 +413,7 @@ class RustGenerator(BaseGenerator):
"""Generate a Rust struct."""
lines = []
- type_name = message.name
+ type_name = self.to_pascal_case(message.name)
# Derive macros
comment = self.format_type_id_comment(message, "//")
@@ -487,7 +487,7 @@ class RustGenerator(BaseGenerator):
def generate_debug_impl(self, message: Message) -> List[str]:
"""Generate a Debug impl that avoids recursive ref expansion."""
lines: List[str] = []
- type_name = message.name
+ type_name = self.to_pascal_case(message.name)
lines.append(f"impl std::fmt::Debug for {type_name} {{")
lines.append(
" fn fmt(&self, f: &mut std::fmt::Formatter<'_>) ->
std::fmt::Result {"
@@ -766,20 +766,20 @@ class RustGenerator(BaseGenerator):
target_parents = parts[:-1]
base_name = parts[-1]
return self.build_relative_type_name(
- current_parents, target_parents, base_name
+ current_parents, target_parents, self.to_pascal_case(base_name)
)
if not parent_stack:
- return type_name
+ return self.to_pascal_case(type_name)
for i in range(len(parent_stack) - 1, -1, -1):
message = parent_stack[i]
if message.get_nested_type(type_name) is not None:
target_parents = [msg.name for msg in parent_stack[: i + 1]]
return self.build_relative_type_name(
- current_parents, target_parents, type_name
+ current_parents, target_parents,
self.to_pascal_case(type_name)
)
- return type_name
+ return self.to_pascal_case(type_name)
def collect_uses(self, field_type: FieldType, uses: Set[str]):
"""Collect required use statements for a field type."""
@@ -921,7 +921,7 @@ class RustGenerator(BaseGenerator):
parent_stack: Optional[List[Message]],
):
"""Generate registration code for a message and its nested types."""
- type_name = self.get_type_path(message.name, parent_stack)
+ type_name = self.get_type_path(self.to_pascal_case(message.name),
parent_stack)
reg_name = self.get_registration_type_name(message.name, parent_stack)
# Register nested enums first
diff --git a/compiler/fory_compiler/tests/test_nested_types.py
b/compiler/fory_compiler/tests/test_nested_types.py
index 8515dcdc7..4dafcb5b7 100644
--- a/compiler/fory_compiler/tests/test_nested_types.py
+++ b/compiler/fory_compiler/tests/test_nested_types.py
@@ -17,10 +17,15 @@
"""Tests for FDL nested type support."""
+from pathlib import Path
+from textwrap import dedent
+
import pytest
from fory_compiler.frontend.fdl.lexer import Lexer
from fory_compiler.frontend.fdl.parser import Parser
+from fory_compiler.generators.base import GeneratorOptions
+from fory_compiler.generators.rust import RustGenerator
from fory_compiler.ir.ast import NamedType, ListType
from fory_compiler.ir.validator import SchemaValidator
@@ -369,5 +374,76 @@ class TestSchemaTypeLookup:
assert "Deep" in type_names
+class TestRustNestedTypeGeneration:
+ """Rust-specific tests for nested type code generation."""
+
+ def _generate_rust(self, source: str) -> str:
+ lexer = Lexer(source)
+ parser = Parser(lexer.tokenize())
+ schema = parser.parse()
+ options = GeneratorOptions(output_dir=Path("/tmp"))
+ generator = RustGenerator(schema, options)
+ files = generator.generate()
+ return "\n".join(f.content for f in files)
+
+ def test_nested_message_no_name_collision(self):
+ """Regression for #3547: struct and module must not share the same
identifier."""
+ source = dedent("""
+ message foo {
+ message Bar {
+ string baz = 1;
+ }
+ Bar bar = 1;
+ }
+ """)
+ content = self._generate_rust(source)
+ assert "pub mod foo {" in content
+ assert "pub struct Foo {" in content
+ assert "pub struct foo" not in content
+
+ def test_nested_message_field_type_qualified(self):
+ """Field referencing a nested type resolves to module-qualified
path."""
+ source = dedent("""
+ message foo {
+ message Bar {
+ string baz = 1;
+ }
+ Bar bar = 1;
+ }
+ """)
+ content = self._generate_rust(source)
+ assert "pub bar: foo::Bar," in content
+
+ def test_nested_message_registration_uses_pascal_struct(self):
+ """register_by_namespace must reference the PascalCase struct name."""
+ source = dedent("""
+ message foo {
+ message Bar {
+ string baz = 1;
+ }
+ Bar bar = 1;
+ }
+ """)
+ content = self._generate_rust(source)
+ assert "register_by_namespace::<Foo>" in content
+ assert 'register_by_namespace::<Foo>("default", "foo")' in content
+ assert "register_by_namespace::<foo::Bar>" in content
+
+ def test_pascal_case_names_unchanged(self):
+ """Messages already in PascalCase must not be renamed."""
+ source = dedent("""
+ message Outer {
+ message Inner {
+ string value = 1;
+ }
+ Inner inner = 1;
+ }
+ """)
+ content = self._generate_rust(source)
+ assert "pub struct Outer {" in content
+ assert "pub struct Inner {" in content
+ assert "pub outer: " not in content
+
+
if __name__ == "__main__":
pytest.main([__file__, "-v"])
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]