diff --git a/compiler/fory_compiler/generators/cpp.py b/compiler/fory_compiler/generators/cpp.py index 4d9d6a937b..edaecd4fef 100644 --- a/compiler/fory_compiler/generators/cpp.py +++ b/compiler/fory_compiler/generators/cpp.py @@ -884,8 +884,9 @@ def generate_bytes_methods(self, class_name: str, indent: str) -> List[str]: lines.append(f"{indent}}}") lines.append("") lines.append( - f"{indent}static ::fory::Result<{class_name}, ::fory::Error> from_bytes(const ::std::vector<::uint8_t>& data) {{" + f"{indent}static ::fory::Result<{class_name}, ::fory::Error> from_bytes(" ) + lines.append(f"{indent} const ::std::vector<::uint8_t>& data) {{") lines.append( f"{indent} return {detail}::get_fory().deserialize<{class_name}>(data);" ) @@ -1589,13 +1590,13 @@ def generate_field_accessors( f"{indent}void set_{field_name}(Arg&& arg, Args&&... args) {{" ) if field.optional: - lines.append( - f"{indent} {member_name}.emplace(::std::forward(arg), ::std::forward(args)...);" - ) + lines.append(f"{indent} {member_name}.emplace(") + lines.append(f"{indent} ::std::forward(arg),") + lines.append(f"{indent} ::std::forward(args)...);") else: - lines.append( - f"{indent} {member_name} = {value_type}(::std::forward(arg), ::std::forward(args)...);" - ) + lines.append(f"{indent} {member_name} = {value_type}(") + lines.append(f"{indent} ::std::forward(arg),") + lines.append(f"{indent} ::std::forward(args)...);") lines.append(f"{indent}}}") else: lines.append(f"{indent}void set_{field_name}({value_type} value) {{") @@ -1680,7 +1681,14 @@ def generate_message_definition( self.get_field_eq_expression(field, lineage) for field in message.fields ] - lines.append(f"{body_indent} return {' && '.join(conditions)};") + return_line = f"{body_indent} return {' && '.join(conditions)};" + if len(return_line) > 80: + lines.append(f"{body_indent} return") + for index, condition in enumerate(conditions): + suffix = " &&" if index + 1 < len(conditions) else ";" + lines.append(f"{body_indent} {condition}{suffix}") + else: + lines.append(return_line) else: lines.append(f"{body_indent} return true;") lines.append(f"{body_indent}}}") @@ -1698,12 +1706,23 @@ def generate_message_definition( lines.append(f"{field_indent}{field_type} {member_name};") lines.append("") lines.append(f"{body_indent}public:") - field_members = ", ".join( + macro_entries = [ self.get_field_macro_entry(message, f) for f in message.fields - ) - lines.append( + ] + field_members = ", ".join(macro_entries) + macro_line = ( f"{body_indent}FORY_STRUCT({struct_type_name}, {field_members});" ) + + if len(macro_line) > 80: + lines.append(f"{body_indent}FORY_STRUCT(") + lines.append(f"{body_indent} {struct_type_name},") + for index, entry in enumerate(macro_entries): + suffix = "," if index + 1 < len(macro_entries) else "" + lines.append(f"{body_indent} {entry}{suffix}") + lines.append(f"{body_indent});") + else: + lines.append(macro_line) else: lines.append(f"{body_indent}FORY_STRUCT({struct_type_name});") @@ -1747,9 +1766,11 @@ def generate_union_definition( self.get_union_case_type(field, parent_stack) for field in union.fields ] case_aliases = [ - f"ForyCase{self.to_pascal_case(field.name)}Type" - if "," in case_type - else None + ( + f"ForyCase{self.to_pascal_case(field.name)}Type" + if "," in case_type + else None + ) for field, case_type in zip(union.fields, raw_case_types) ] case_types = [ diff --git a/compiler/fory_compiler/generators/go.py b/compiler/fory_compiler/generators/go.py index 383dd8a6e3..7d24002267 100644 --- a/compiler/fory_compiler/generators/go.py +++ b/compiler/fory_compiler/generators/go.py @@ -1525,9 +1525,11 @@ def generate_registration(self) -> List[str]: def generate_fory_helpers(self) -> List[str]: lines: List[str] = [] lines.append("func createFory() *fory.Fory {") - lines.append( - "\tf := fory.New(fory.WithXlang(true), fory.WithRefTracking(true), fory.WithCompatible(true))" - ) + lines.append("\tf := fory.New(") + lines.append("\t\tfory.WithXlang(true),") + lines.append("\t\tfory.WithRefTracking(true),") + lines.append("\t\tfory.WithCompatible(true),") + lines.append("\t)") for alias, _, _ in self._collect_imported_type_infos(): lines.append(f"\tif err := {alias}.RegisterTypes(f); err != nil {{") lines.append("\t\tpanic(err)") diff --git a/compiler/fory_compiler/generators/java.py b/compiler/fory_compiler/generators/java.py index 9b9b7906d8..7ee72f3a12 100644 --- a/compiler/fory_compiler/generators/java.py +++ b/compiler/fory_compiler/generators/java.py @@ -2180,9 +2180,12 @@ def generate_module_file( lines.append(" }") lines.append("") lines.append(" private static ThreadSafeFory createFory() {") - lines.append( - " return Fory.builder().withXlang(true).withCompatible(true).withRefTracking(true).withModule(INSTANCE).buildThreadSafeFory();" - ) + lines.append(" return Fory.builder()") + lines.append(" .withXlang(true)") + lines.append(" .withCompatible(true)") + lines.append(" .withRefTracking(true)") + lines.append(" .withModule(INSTANCE)") + lines.append(" .buildThreadSafeFory();") lines.append(" }") lines.append("") lines.append(" private static class Holder {") diff --git a/compiler/fory_compiler/generators/python.py b/compiler/fory_compiler/generators/python.py index 442c3c7ba1..f13cabff26 100644 --- a/compiler/fory_compiler/generators/python.py +++ b/compiler/fory_compiler/generators/python.py @@ -623,14 +623,26 @@ def generate_field( field_args.append(f"default_factory={default_factory}") else: field_args.append(f"default={default_expr}") - field_default = f"pyfory.field({', '.join(field_args)}){trailing_comment}" + + field_line = ( + f"{field_name}: {python_type} = " + f"pyfory.field({', '.join(field_args)}){trailing_comment}" + ) + + if len(f" {field_line}") > 80: + lines.append(f"{field_name}: {python_type} = pyfory.field(") + for arg in field_args: + lines.append(f" {arg},") + lines.append(f"){trailing_comment}") + else: + lines.append(field_line) else: if default_factory is not None: field_default = f"field(default_factory={default_factory})" else: field_default = f"{default_expr}{trailing_comment}" - lines.append(f"{field_name}: {python_type} = {field_default}") + lines.append(f"{field_name}: {python_type} = {field_default}") return lines @@ -683,7 +695,14 @@ def generate_repr_method(self, message: Message, indent: int = 0) -> List[str]: expr = f"({placeholder_literal} if self.{field_name} is not None else 'None')" else: expr = f"repr(self.{field_name})" - lines.append(f'{ind} parts.append("{field_name}=" + {expr})') + line = f'{ind} parts.append("{field_name}=" + {expr})' + if len(line) > 80: + lines.append(f"{ind} parts.append(") + lines.append(f'{ind} "{field_name}="') + lines.append(f"{ind} + {expr}") + lines.append(f"{ind} )") + else: + lines.append(line) lines.append(f'{ind} return "{message.name}(" + ", ".join(parts) + ")"') return lines @@ -1013,9 +1032,9 @@ def generate_fory_helpers(self) -> List[str]: lines.append(" if _threadsafe_fory is None:") lines.append(" with _fory_lock:") lines.append(" if _threadsafe_fory is None:") - lines.append( - " _threadsafe_fory = pyfory.ThreadSafeFory(fory_factory=_create_fory)" - ) + lines.append(" _threadsafe_fory = pyfory.ThreadSafeFory(") + lines.append(" fory_factory=_create_fory") + lines.append(" )") lines.append(" return _threadsafe_fory") return lines diff --git a/compiler/fory_compiler/generators/rust.py b/compiler/fory_compiler/generators/rust.py index 54ba2f7588..230a8450b4 100644 --- a/compiler/fory_compiler/generators/rust.py +++ b/compiler/fory_compiler/generators/rust.py @@ -615,16 +615,18 @@ def _format_imported_type_name( def generate_bytes_impl(self, type_name: str) -> List[str]: lines = [] lines.append(f"impl {type_name} {{") + lines.append(" pub fn to_bytes(") + lines.append(" &self,") lines.append( - " pub fn to_bytes(&self) -> ::std::result::Result<::std::vec::Vec, ::fory::Error> {" + " ) -> ::std::result::Result<::std::vec::Vec, ::fory::Error> {" ) lines.append(" let fory = detail::get_fory();") lines.append(" fory.serialize(self)") lines.append(" }") lines.append("") - lines.append( - f" pub fn from_bytes(data: &[u8]) -> ::std::result::Result<{type_name}, ::fory::Error> {{" - ) + lines.append(" pub fn from_bytes(") + lines.append(" data: &[u8],") + lines.append(f" ) -> ::std::result::Result<{type_name}, ::fory::Error> {{") lines.append(" let fory = detail::get_fory();") lines.append(" fory.deserialize(data)") lines.append(" }") @@ -1364,7 +1366,22 @@ def generate_field( ) field_name = self.get_field_identifier(parent_stack[-1], field) - lines.append(f"pub {field_name}: {rust_type},") + field_line = f"pub {field_name}: {rust_type}," + + if len(f" {field_line}") > 80 and rust_type.startswith( + "::std::collections::HashMap<" + ): + inner_type = rust_type.removeprefix( + "::std::collections::HashMap<" + ).removesuffix(">") + key_type, value_type = inner_type.split(", ", 1) + + lines.append(f"pub {field_name}: ::std::collections::HashMap<") + lines.append(f" {key_type},") + lines.append(f" {value_type},") + lines.append(">,") + else: + lines.append(field_line) return lines @@ -1622,9 +1639,9 @@ def generate_registration(self) -> List[str]: """Generate the Fory registration function.""" lines = [] - lines.append( - "pub fn register_types(fory: &mut ::fory::Fory) -> ::std::result::Result<(), ::fory::Error> {" - ) + lines.append("pub fn register_types(") + lines.append(" fory: &mut ::fory::Fory,") + lines.append(") -> ::std::result::Result<(), ::fory::Error> {") # Register enums (top-level) for enum in self.schema.enums: @@ -1655,9 +1672,8 @@ def generate_fory_helpers(self) -> List[str]: lines.append(" use super::*;") lines.append("") lines.append(" pub(super) fn get_fory() -> &'static ::fory::Fory {") - lines.append( - " static FORY: ::std::sync::OnceLock<::fory::Fory> = ::std::sync::OnceLock::new();" - ) + lines.append(" static FORY: ::std::sync::OnceLock<::fory::Fory> =") + lines.append(" ::std::sync::OnceLock::new();") lines.append(" FORY.get_or_init(|| {") lines.append(" let mut fory = ::fory::Fory::builder()") lines.append(" .xlang(true)") diff --git a/compiler/fory_compiler/tests/test_generated_code.py b/compiler/fory_compiler/tests/test_generated_code.py index 51ba17632a..d7dcf0b858 100644 --- a/compiler/fory_compiler/tests/test_generated_code.py +++ b/compiler/fory_compiler/tests/test_generated_code.py @@ -231,9 +231,12 @@ def test_rust_nested_container_ref_uses_correct_pointer_type(): ) assert "::std::vec::Vec<::std::vec::Vec<::std::rc::Rc>>" not in rust_output assert ( - "pub nodes: ::std::collections::HashMap<::std::string::String, " - "::std::collections::HashMap<::std::string::String, ::std::sync::Arc>>," - in rust_output + " pub nodes: ::std::collections::HashMap<\n" + " ::std::string::String,\n" + " ::std::collections::HashMap<" + "::std::string::String, " + "::std::sync::Arc>,\n" + " >," in rust_output ) assert ( "::std::collections::HashMap<::std::string::String, " @@ -1095,10 +1098,13 @@ def test_cpp_nested_integer_specs_in_generic_containers(): ) cpp_output = render_files(generate_files(schema, CppGenerator)) assert ( - "FORY_STRUCT(NestedIntegerSpecs, " - "(values_, ::fory::F(1).map(::fory::T::uint32().fixed(), " - "::fory::T::list(::fory::T::inner(::fory::T::uint64().tagged())))));" - in cpp_output + " FORY_STRUCT(\n" + " NestedIntegerSpecs,\n" + " (values_, ::fory::F(1).map(" + "::fory::T::uint32().fixed(), " + "::fory::T::list(" + "::fory::T::inner(::fory::T::uint64().tagged()))))\n" + " );" in cpp_output ) @@ -1662,15 +1668,21 @@ def test_rust_generated_code_uses_absolute_paths(): assert "pub value: ::std::string::String," in rust_output assert "pub items: ::std::vec::Vec<::std::string::String>," in rust_output assert ( - "pub labels: ::std::collections::HashMap<::std::string::String, ::std::string::String>," - in rust_output + " pub labels: ::std::collections::HashMap<\n" + " ::std::string::String,\n" + " ::std::string::String,\n" + " >," in rust_output ) assert ( "pub payload: ::std::sync::Arc," in rust_output ) assert "pub parent: ::fory::ArcWeak," in rust_output - assert "pub fn register_types(fory: &mut ::fory::Fory)" in rust_output + assert ( + "pub fn register_types(\n" + " fory: &mut ::fory::Fory,\n" + ") -> ::std::result::Result<(), ::fory::Error> {" in rust_output + ) assert "static FORY: ::std::sync::OnceLock<::fory::Fory>" in rust_output @@ -1807,3 +1819,129 @@ def test_rust_rejects_same_output_path_collisions( assert exit_code == 1 assert "Rust output path collision" in captured.err + + +def test_java_generated_helper_builder_is_wrapped(): + schema = parse_fdl( + dedent( + """ + package gen; + + message Person { + string first_name = 1; + } + """ + ) + ) + + java_output = render_files(generate_files(schema, JavaGenerator)) + + assert ( + "return Fory.builder()\n" + " .withXlang(true)\n" + " .withCompatible(true)\n" + " .withRefTracking(true)\n" + " .withModule(INSTANCE)\n" + " .buildThreadSafeFory();" in java_output + ) + + +def test_go_generated_helper_options_are_wrapped(): + schema = parse_fdl( + dedent( + """ + package gen; + + message Person { + string first_name = 1; + } + """ + ) + ) + + go_output = render_files(generate_files(schema, GoGenerator)) + + assert ( + "f := fory.New(\n" + "\t\tfory.WithXlang(true),\n" + "\t\tfory.WithRefTracking(true),\n" + "\t\tfory.WithCompatible(true),\n" + "\t)" in go_output + ) + + +def test_python_field_wrapping_is_generated(): + schema = parse_fdl( + dedent( + """ + package gen; + + message Person { + map very_long_metadata_field_name_for_wrapping = 1; + } + """ + ) + ) + + python_output = render_files(generate_files(schema, PythonGenerator)) + + assert ( + " very_long_metadata_field_name_for_wrapping: " + "Dict[str, str] = pyfory.field(\n" + " id=1,\n" + " default_factory=dict,\n" + " )" in python_output + ) + + +def test_cpp_equality_and_struct_macros_are_wrapped(): + schema = parse_fdl( + dedent( + """ + package gen; + + message Person { + string first_name = 1; + string last_name = 2; + string email_address = 3; + list tags = 4; + map metadata = 5; + } + """ + ) + ) + + cpp_output = render_files(generate_files(schema, CppGenerator)) + + assert " return\n" in cpp_output + assert " first_name_ == other.first_name_ &&" in cpp_output + assert " FORY_STRUCT(\n" in cpp_output + assert " Person,\n" in cpp_output + + +def test_rust_map_fields_and_helpers_are_wrapped(): + schema = parse_fdl( + dedent( + """ + package gen; + + message Person { + map metadata = 1; + } + """ + ) + ) + + rust_output = render_files(generate_files(schema, RustGenerator)) + + assert ( + " pub metadata: ::std::collections::HashMap<\n" + " ::std::string::String,\n" + " ::std::string::String,\n" + " >," in rust_output + ) + assert ( + "pub fn register_types(\n" + " fory: &mut ::fory::Fory,\n" + ") -> ::std::result::Result<(), ::fory::Error> {" in rust_output + )