diff --git a/Cargo.lock b/Cargo.lock index 68a7f2e..76a0a84 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -198,6 +198,7 @@ dependencies = [ "jetted_target_csharp_system_text", "jetted_target_go", "jetted_target_java_jackson", + "jetted_target_php", "jetted_target_python", "jetted_target_ruby", "jetted_target_ruby_sig", @@ -250,6 +251,16 @@ dependencies = [ "serde_json", ] +[[package]] +name = "jetted_target_php" +version = "0.1.0" +dependencies = [ + "jetted_core", + "jetted_test", + "regex", + "serde_json", +] + [[package]] name = "jetted_target_python" version = "0.1.0" diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index f708850..468a343 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -21,6 +21,7 @@ jetted_target_ruby = { path = "../target_ruby" } jetted_target_ruby_sig = { path = "../target_ruby_sig" } jetted_target_rust = { path = "../target_rust" } jetted_target_typescript = { path = "../target_typescript" } +jetted_target_php = { path = "../target_php" } serde = "1.0" serde_json = "1.0" jtd = "0.2.1" diff --git a/crates/cli/src/cli.yaml b/crates/cli/src/cli.yaml index 8a9eb46..e5c7367 100644 --- a/crates/cli/src/cli.yaml +++ b/crates/cli/src/cli.yaml @@ -110,3 +110,17 @@ args: long: typescript-out takes_value: true value_name: dir + + # PHP + - php-out: + help: Output directory for PHP code generation + long: php-out + takes_value: true + value_name: dir + requires: + - php-namespace + - php-namespace: + help: Namespace for PHP generated types + long: php-namespace + takes_value: true + value_name: namespace diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 1d0b00f..1fc4e5e 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -155,6 +155,20 @@ fn main() -> Result<()> { log.finish("TypeScript", &codegen_info); } + if let Some(out_dir) = matches.value_of("php-out") { + log.start("PHP", out_dir); + + let namespace = matches.value_of("php-namespace").unwrap().to_owned(); + + let target = jetted_target_php::Target::new(Some(namespace)); + + let codegen_info = + jetted_core::codegen(&target, root_name.clone(), &schema, &Path::new(out_dir)) + .with_context(|| "Failed to generate PHP code")?; + + log.finish("PHP", &codegen_info); + } + log.flush(); Ok(()) } diff --git a/crates/core/src/target/inflect.rs b/crates/core/src/target/inflect.rs index 394f063..f7f7e5f 100644 --- a/crates/core/src/target/inflect.rs +++ b/crates/core/src/target/inflect.rs @@ -35,7 +35,7 @@ pub struct CombiningInflector { } impl CombiningInflector { - pub fn new(case: Case) -> Self { + pub const fn new(case: Case) -> Self { Self { case } } } @@ -51,7 +51,7 @@ pub struct TailInflector { } impl TailInflector { - pub fn new(case: Case) -> Self { + pub const fn new(case: Case) -> Self { Self { case } } } @@ -104,7 +104,7 @@ pub struct Case { } impl Case { - pub fn new( + pub const fn new( first_capitalization: CaseCapitalization, rest_capitalization: CaseCapitalization, delimiter: Option, @@ -118,7 +118,7 @@ impl Case { } } - pub fn camel_case() -> Self { + pub const fn camel_case() -> Self { Self::new( CaseCapitalization::None, CaseCapitalization::Initial, @@ -127,7 +127,7 @@ impl Case { ) } - pub fn pascal_case() -> Self { + pub const fn pascal_case() -> Self { Self::new( CaseCapitalization::Initial, CaseCapitalization::Initial, @@ -136,7 +136,7 @@ impl Case { ) } - pub fn pascal_case_with_initialisms(initialisms: BTreeSet) -> Self { + pub const fn pascal_case_with_initialisms(initialisms: BTreeSet) -> Self { Self::new( CaseCapitalization::Initial, CaseCapitalization::Initial, @@ -145,7 +145,7 @@ impl Case { ) } - pub fn snake_case() -> Self { + pub const fn snake_case() -> Self { Self::new( CaseCapitalization::None, CaseCapitalization::None, @@ -154,7 +154,7 @@ impl Case { ) } - pub fn screaming_snake_case() -> Self { + pub const fn screaming_snake_case() -> Self { Self::new( CaseCapitalization::All, CaseCapitalization::All, diff --git a/crates/target_php/Cargo.toml b/crates/target_php/Cargo.toml new file mode 100644 index 0000000..f10c729 --- /dev/null +++ b/crates/target_php/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "jetted_target_php" +version = "0.1.0" +authors = [ + "Eugenio Tampieri", +] +edition = "2024" + +[dependencies] +jetted_core = { path = "../core" } +serde_json = "1.0" +regex = "1" + +[dev-dependencies] +jetted_test = { path = "../test" } diff --git a/crates/target_php/output/basic_discriminator/BasicDiscriminator.php b/crates/target_php/output/basic_discriminator/BasicDiscriminator.php new file mode 100644 index 0000000..b3d9bbc --- /dev/null +++ b/crates/target_php/output/basic_discriminator/BasicDiscriminator.php @@ -0,0 +1 @@ +bar = $bar; + $this->baz = $baz; + $this->foo = $foo; + $this->quux = $quux; + } + public function serialize() { + $result = []; + $result["bar"] = $this->bar; + $result["baz"] = $this->baz; + $result["foo"] = $this->foo; + $result["quux"] = $this->quux; + return json_encode($result); + } + public static function deserialize($data) { + return new self( + $result["bar"], + $result["baz"], + $result["foo"], + $result["quux"] + ); + } +} diff --git a/crates/target_php/output/custom_overrides/CustomOverrides.php b/crates/target_php/output/custom_overrides/CustomOverrides.php new file mode 100644 index 0000000..4cab586 --- /dev/null +++ b/crates/target_php/output/custom_overrides/CustomOverrides.php @@ -0,0 +1,46 @@ +override_elements_container = $override_elements_container; + $this->override_type_discriminator = $override_type_discriminator; + $this->override_type_enum = $override_type_enum; + $this->override_type_expr = $override_type_expr; + $this->override_type_properties = $override_type_properties; + $this->override_values_container = $override_values_container; + } + public function serialize() { + $result = []; + $result["override_elements_container"] = $this->override_elements_container; + $result["override_type_discriminator"] = $this->override_type_discriminator; + $result["override_type_enum"] = $this->override_type_enum; + $result["override_type_expr"] = $this->override_type_expr; + $result["override_type_properties"] = $this->override_type_properties; + $result["override_values_container"] = $this->override_values_container; + return json_encode($result); + } + public static function deserialize($data) { + return new self( + $result["override_elements_container"], + $result["override_type_discriminator"], + $result["override_type_enum"], + $result["override_type_expr"], + $result["override_type_properties"], + $result["override_values_container"] + ); + } +} diff --git a/crates/target_php/output/custom_overrides/CustomOverridesOverrideTypeDiscriminator.php b/crates/target_php/output/custom_overrides/CustomOverridesOverrideTypeDiscriminator.php new file mode 100644 index 0000000..b3d9bbc --- /dev/null +++ b/crates/target_php/output/custom_overrides/CustomOverridesOverrideTypeDiscriminator.php @@ -0,0 +1 @@ +discriminator_with_description = $discriminator_with_description; + $this->enum_with_description = $enum_with_description; + $this->long_description = $long_description; + $this->properties_with_description = $properties_with_description; + $this->ref_with_description = $ref_with_description; + $this->string_with_description = $string_with_description; + } + public function serialize() { + $result = []; + $result["discriminator_with_description"] = $this->discriminator_with_description; + $result["enum_with_description"] = $this->enum_with_description; + $result["long_description"] = $this->long_description; + $result["properties_with_description"] = $this->properties_with_description; + $result["ref_with_description"] = $this->ref_with_description; + $result["string_with_description"] = $this->string_with_description; + return json_encode($result); + } + public static function deserialize($data) { + return new self( + $result["discriminator_with_description"], + $result["enum_with_description"], + $result["long_description"], + $result["properties_with_description"], + $result["ref_with_description"], + $result["string_with_description"] + ); + } +} diff --git a/crates/target_php/output/description/DescriptionDiscriminatorWithDescription.php b/crates/target_php/output/description/DescriptionDiscriminatorWithDescription.php new file mode 100644 index 0000000..b3d9bbc --- /dev/null +++ b/crates/target_php/output/description/DescriptionDiscriminatorWithDescription.php @@ -0,0 +1 @@ +default_name = $default_name; + $this->foo = $foo; + $this->foo0 = $foo0; + $this->foo1 = $foo1; + $this->foo_bar = $foo_bar; + $this->foo_bar0 = $foo_bar0; + $this->foo0bar = $foo0bar; + $this->foo_bar1 = $foo_bar1; + } + public function serialize() { + $result = []; + $result[""] = $this->default_name; + $result["$foo"] = $this->foo; + $result["0foo"] = $this->foo0; + $result["_foo"] = $this->foo1; + $result["foo +bar"] = $this->foo_bar; + $result["foo bar"] = $this->foo_bar0; + $result["foo0bar"] = $this->foo0bar; + $result["foo﷽bar"] = $this->foo_bar1; + return json_encode($result); + } + public static function deserialize($data) { + return new self( + $result[""], + $result["$foo"], + $result["0foo"], + $result["_foo"], + $result["foo +bar"], + $result["foo bar"], + $result["foo0bar"], + $result["foo﷽bar"] + ); + } +} diff --git a/crates/target_php/output/enum_collisions/EnumCollisions.php b/crates/target_php/output/enum_collisions/EnumCollisions.php new file mode 100644 index 0000000..61ea9f0 --- /dev/null +++ b/crates/target_php/output/enum_collisions/EnumCollisions.php @@ -0,0 +1,26 @@ +foo = $foo; + $this->foo_bar = $foo_bar; + } + public function serialize() { + $result = []; + $result["foo"] = $this->foo; + $result["foo_bar"] = $this->foo_bar; + return json_encode($result); + } + public static function deserialize($data) { + return new self( + $result["foo"], + $result["foo_bar"] + ); + } +} diff --git a/crates/target_php/output/enum_collisions/EnumCollisionsFoo.php b/crates/target_php/output/enum_collisions/EnumCollisionsFoo.php new file mode 100644 index 0000000..1bb4d83 --- /dev/null +++ b/crates/target_php/output/enum_collisions/EnumCollisionsFoo.php @@ -0,0 +1,21 @@ +bar = $bar; + } + public function serialize() { + $result = []; + $result["bar"] = $this->bar; + return json_encode($result); + } + public static function deserialize($data) { + return new self( + $result["bar"] + ); + } +} diff --git a/crates/target_php/output/enum_collisions/EnumCollisionsFooBar.php b/crates/target_php/output/enum_collisions/EnumCollisionsFooBar.php new file mode 100644 index 0000000..b3d9bbc --- /dev/null +++ b/crates/target_php/output/enum_collisions/EnumCollisionsFooBar.php @@ -0,0 +1 @@ +http = $http; + $this->id = $id; + $this->nested_id_initialism = $nested_id_initialism; + $this->utf8 = $utf8; + $this->word_with_embedded_id_initialism = $word_with_embedded_id_initialism; + $this->word_with_trailing_initialism_id = $word_with_trailing_initialism_id; + } + public function serialize() { + $result = []; + $result["http"] = $this->http; + $result["id"] = $this->id; + $result["nested_id_initialism"] = $this->nested_id_initialism; + $result["utf8"] = $this->utf8; + $result["word_with_embedded_id_initialism"] = $this->word_with_embedded_id_initialism; + $result["word_with_trailing_initialism_id"] = $this->word_with_trailing_initialism_id; + return json_encode($result); + } + public static function deserialize($data) { + return new self( + $result["http"], + $result["id"], + $result["nested_id_initialism"], + $result["utf8"], + $result["word_with_embedded_id_initialism"], + $result["word_with_trailing_initialism_id"] + ); + } +} diff --git a/crates/target_php/output/initialisms/InitialismsNestedIdInitialism.php b/crates/target_php/output/initialisms/InitialismsNestedIdInitialism.php new file mode 100644 index 0000000..0f82062 --- /dev/null +++ b/crates/target_php/output/initialisms/InitialismsNestedIdInitialism.php @@ -0,0 +1,26 @@ +json = $json; + $this->normalword = $normalword; + } + public function serialize() { + $result = []; + $result["json"] = $this->json; + $result["normalword"] = $this->normalword; + return json_encode($result); + } + public static function deserialize($data) { + return new self( + $result["json"], + $result["normalword"] + ); + } +} diff --git a/crates/target_php/output/keywords/For.php b/crates/target_php/output/keywords/For.php new file mode 100644 index 0000000..b3d9bbc --- /dev/null +++ b/crates/target_php/output/keywords/For.php @@ -0,0 +1 @@ +for = $for; + $this->object = $object; + } + public function serialize() { + $result = []; + $result["for"] = $this->for; + $result["object"] = $this->object; + return json_encode($result); + } + public static function deserialize($data) { + return new self( + $result["for"], + $result["object"] + ); + } +} diff --git a/crates/target_php/output/keywords/Object.php b/crates/target_php/output/keywords/Object.php new file mode 100644 index 0000000..b3d9bbc --- /dev/null +++ b/crates/target_php/output/keywords/Object.php @@ -0,0 +1 @@ +bar = $bar; + $this->baz = $baz; + $this->foo = $foo; + $this->quux = $quux; + } + public function serialize() { + $result = []; + $result["bar"] = $this->bar; + $result["baz"] = $this->baz; + $result["foo"] = $this->foo; + $result["quux"] = $this->quux; + return json_encode($result); + } + public static function deserialize($data) { + return new self( + $result["bar"], + $result["baz"], + $result["foo"], + $result["quux"] + ); + } +} diff --git a/crates/target_php/output/nullable_references/NotnullRefNotnullString.php b/crates/target_php/output/nullable_references/NotnullRefNotnullString.php new file mode 100644 index 0000000..b3d9bbc --- /dev/null +++ b/crates/target_php/output/nullable_references/NotnullRefNotnullString.php @@ -0,0 +1 @@ +notnull_ref_notnull_string = $notnull_ref_notnull_string; + $this->notnull_ref_null_string = $notnull_ref_null_string; + $this->notnull_string = $notnull_string; + $this->null_ref_notnull_string = $null_ref_notnull_string; + $this->null_ref_null_string = $null_ref_null_string; + $this->null_string = $null_string; + } + public function serialize() { + $result = []; + $result["notnull_ref_notnull_string"] = $this->notnull_ref_notnull_string; + $result["notnull_ref_null_string"] = $this->notnull_ref_null_string; + $result["notnull_string"] = $this->notnull_string; + $result["null_ref_notnull_string"] = $this->null_ref_notnull_string; + $result["null_ref_null_string"] = $this->null_ref_null_string; + $result["null_string"] = $this->null_string; + return json_encode($result); + } + public static function deserialize($data) { + return new self( + $result["notnull_ref_notnull_string"], + $result["notnull_ref_null_string"], + $result["notnull_string"], + $result["null_ref_notnull_string"], + $result["null_ref_null_string"], + $result["null_string"] + ); + } +} diff --git a/crates/target_php/output/nullable_timestamp_property/NullableTimestampProperty.php b/crates/target_php/output/nullable_timestamp_property/NullableTimestampProperty.php new file mode 100644 index 0000000..e5c7ac8 --- /dev/null +++ b/crates/target_php/output/nullable_timestamp_property/NullableTimestampProperty.php @@ -0,0 +1,21 @@ +foo = $foo; + } + public function serialize() { + $result = []; + $result["foo"] = $this->foo; + return json_encode($result); + } + public static function deserialize($data) { + return new self( + $result["foo"] + ); + } +} diff --git a/crates/target_php/output/optional_properties/OptionalProperties.php b/crates/target_php/output/optional_properties/OptionalProperties.php new file mode 100644 index 0000000..89c4d1b --- /dev/null +++ b/crates/target_php/output/optional_properties/OptionalProperties.php @@ -0,0 +1,31 @@ +bar = $bar; + $this->baz = $baz; + $this->foo = $foo; + } + public function serialize() { + $result = []; + $result["bar"] = $this->bar; + $result["baz"] = $this->baz; + $result["foo"] = $this->foo; + return json_encode($result); + } + public static function deserialize($data) { + return new self( + $result["bar"], + $result["baz"], + $result["foo"] + ); + } +} diff --git a/crates/target_php/output/property_name_collisions/PropertyNameCollisions.php b/crates/target_php/output/property_name_collisions/PropertyNameCollisions.php new file mode 100644 index 0000000..ed23b8f --- /dev/null +++ b/crates/target_php/output/property_name_collisions/PropertyNameCollisions.php @@ -0,0 +1,26 @@ +foo = $foo; + $this->foo0 = $foo0; + } + public function serialize() { + $result = []; + $result["Foo"] = $this->foo; + $result["foo"] = $this->foo0; + return json_encode($result); + } + public static function deserialize($data) { + return new self( + $result["Foo"], + $result["foo"] + ); + } +} diff --git a/crates/target_php/output/reference/Bar.php b/crates/target_php/output/reference/Bar.php new file mode 100644 index 0000000..b3d9bbc --- /dev/null +++ b/crates/target_php/output/reference/Bar.php @@ -0,0 +1 @@ +foo = $foo; + $this->foo_bar = $foo_bar; + } + public function serialize() { + $result = []; + $result["foo"] = $this->foo; + $result["foo_bar"] = $this->foo_bar; + return json_encode($result); + } + public static function deserialize($data) { + return new self( + $result["foo"], + $result["foo_bar"] + ); + } +} diff --git a/crates/target_php/output/type_collisions/TypeCollisionsFoo.php b/crates/target_php/output/type_collisions/TypeCollisionsFoo.php new file mode 100644 index 0000000..66ffff4 --- /dev/null +++ b/crates/target_php/output/type_collisions/TypeCollisionsFoo.php @@ -0,0 +1,21 @@ +bar = $bar; + } + public function serialize() { + $result = []; + $result["bar"] = $this->bar; + return json_encode($result); + } + public static function deserialize($data) { + return new self( + $result["bar"] + ); + } +} diff --git a/crates/target_php/output/type_collisions/TypeCollisionsFooBar.php b/crates/target_php/output/type_collisions/TypeCollisionsFooBar.php new file mode 100644 index 0000000..e0041da --- /dev/null +++ b/crates/target_php/output/type_collisions/TypeCollisionsFooBar.php @@ -0,0 +1,21 @@ +x = $x; + } + public function serialize() { + $result = []; + $result["x"] = $this->x; + return json_encode($result); + } + public static function deserialize($data) { + return new self( + $result["x"] + ); + } +} diff --git a/crates/target_php/output/type_collisions/TypeCollisionsFooBar0.php b/crates/target_php/output/type_collisions/TypeCollisionsFooBar0.php new file mode 100644 index 0000000..60cc166 --- /dev/null +++ b/crates/target_php/output/type_collisions/TypeCollisionsFooBar0.php @@ -0,0 +1,21 @@ +x = $x; + } + public function serialize() { + $result = []; + $result["x"] = $this->x; + return json_encode($result); + } + public static function deserialize($data) { + return new self( + $result["x"] + ); + } +} diff --git a/crates/target_php/output/values/Values.php b/crates/target_php/output/values/Values.php new file mode 100644 index 0000000..b3d9bbc --- /dev/null +++ b/crates/target_php/output/values/Values.php @@ -0,0 +1 @@ + String { + " ".repeat(nesting_level.into()) +} + +macro_rules! nested_writeln { + ($stream:expr, $nl:expr, $fmt:expr $(, $arg:expr)*) => {{ + write!($stream, "{}", nest($nl))?; + writeln!($stream, $fmt $(, $arg)*)?; + Ok::<(), std::io::Error>(()) + }}; +} +fn doc_comment( + out: &mut dyn std::io::Write, + nesting_level: u8, + fields: &[Field], +) -> std::io::Result<()> { + nested_writeln!(out, nesting_level, "/**")?; + for field in fields { + nested_writeln!( + out, + nesting_level, + " * @param {}: {}", + field.name, + field.type_ + )?; + } + nested_writeln!(out, nesting_level, " */")?; + + Ok(()) +} +pub fn generate_constructor( + out: &mut dyn std::io::Write, + nesting_level: u8, + fields: &[Field], +) -> std::io::Result<()> { + doc_comment(out, nesting_level, fields)?; + nested_writeln!( + out, + nesting_level, + "public function __construct({}) {{", + fields + .iter() + .map(|x| format!("${}", x.name)) + .collect::>() + .join(", ") + )?; + for field in fields { + nested_writeln!(out, nesting_level + 1, "$this->{0} = ${0};", field.name)?; + } + nested_writeln!(out, nesting_level, "}}")?; + Ok(()) +} + +pub fn generate_serializer( + out: &mut dyn std::io::Write, + nesting_level: u8, + fields: &[Field], +) -> std::io::Result<()> { + nested_writeln!(out, nesting_level, "public function serialize() {{")?; + nested_writeln!(out, nesting_level + 1, "$result = [];")?; + + for field in fields { + nested_writeln!( + out, + nesting_level + 1, + "$result[\"{}\"] = $this->{};", + field.json_name, + field.name + )?; + } + nested_writeln!(out, nesting_level + 1, "return $result;")?; + nested_writeln!(out, nesting_level, "}}")?; + Ok(()) +} + +pub fn generate_deserializer( + out: &mut dyn std::io::Write, + nesting_level: u8, + fields: &[Field], +) -> std::io::Result<()> { + nested_writeln!( + out, + nesting_level, + "public static function deserialize($data) {{" + )?; + + nested_writeln!(out, nesting_level + 1, "return new self(")?; + for (i, field) in fields.into_iter().enumerate() { + let separator = if i == fields.len() - 1 { "" } else { "," }; + nested_writeln!( + out, + nesting_level + 2, + "$data[\"{}\"]{}", + field.json_name, + separator + )?; + } + nested_writeln!(out, nesting_level + 1, ");")?; + nested_writeln!(out, nesting_level, "}}")?; + Ok(()) +} + +pub fn generate_getters( + out: &mut dyn std::io::Write, + nesting_level: u8, + fields: &[Field], +) -> std::io::Result<()> { + for field in fields { + nested_writeln!( + out, + nesting_level, + "public function get_{}() {{", + field.name + )?; + nested_writeln!(out, nesting_level + 1, "return $this->{};", field.name)?; + nested_writeln!(out, nesting_level, "}}")?; + } + Ok(()) +} diff --git a/crates/target_php/src/lib.rs b/crates/target_php/src/lib.rs new file mode 100644 index 0000000..5ab6cbb --- /dev/null +++ b/crates/target_php/src/lib.rs @@ -0,0 +1,171 @@ +use jetted_core::target::inflect::{self, Inflector}; + +mod codegen; + +const TYPE_INFLECTOR: inflect::CombiningInflector = + inflect::CombiningInflector::new(inflect::Case::pascal_case()); +const FIELD_INFLECTOR: inflect::TailInflector = + inflect::TailInflector::new(inflect::Case::snake_case()); +pub struct Target { + namespace: Option, +} +impl Target { + pub fn new(namespace: Option) -> Self { + Self { namespace } + } +} + +impl jetted_core::target::Target for Target { + type FileState = (); + + fn strategy(&self) -> jetted_core::target::Strategy { + jetted_core::target::Strategy { + file_partitioning: jetted_core::target::FilePartitioningStrategy::FilePerType( + "php".to_owned(), + ), + enum_member_naming: jetted_core::target::EnumMemberNamingStrategy::Modularized, + optional_property_handling: + jetted_core::target::OptionalPropertyHandlingStrategy::NativeSupport, + booleans_are_nullable: true, + int8s_are_nullable: true, + uint8s_are_nullable: true, + int16s_are_nullable: true, + uint16s_are_nullable: true, + int32s_are_nullable: true, + uint32s_are_nullable: true, + float32s_are_nullable: true, + float64s_are_nullable: true, + strings_are_nullable: true, + timestamps_are_nullable: true, + arrays_are_nullable: true, + dicts_are_nullable: true, + aliases_are_nullable: true, + enums_are_nullable: true, + structs_are_nullable: true, + discriminators_are_nullable: true, + } + } + + fn name(&self, kind: jetted_core::target::NameableKind, name_parts: &[String]) -> String { + match kind { + jetted_core::target::NameableKind::Type => TYPE_INFLECTOR.inflect(name_parts), + jetted_core::target::NameableKind::Field => FIELD_INFLECTOR.inflect(name_parts), + jetted_core::target::NameableKind::EnumMember => TYPE_INFLECTOR.inflect(name_parts), + } + } + + fn expr( + &self, + state: &mut Self::FileState, + metadata: jetted_core::target::metadata::Metadata, + expr: jetted_core::target::Expr, + ) -> String { + match expr { + jetted_core::target::Expr::Empty => "", + jetted_core::target::Expr::Boolean => "boolean", + jetted_core::target::Expr::Int8 => "int", + jetted_core::target::Expr::Uint8 => "int", + jetted_core::target::Expr::Int16 => "int", + jetted_core::target::Expr::Uint16 => "int", + jetted_core::target::Expr::Int32 => "int", + jetted_core::target::Expr::Uint32 => "int", + jetted_core::target::Expr::Float32 => "float", + jetted_core::target::Expr::Float64 => "float", + jetted_core::target::Expr::String => "string", + jetted_core::target::Expr::Timestamp => "", + jetted_core::target::Expr::ArrayOf(_) => "mixed", + jetted_core::target::Expr::DictOf(_) => "mixed", + jetted_core::target::Expr::NullableOf(_) => "", + } + .to_owned() + } + + fn item( + &self, + out: &mut dyn std::io::Write, + state: &mut Self::FileState, + item: jetted_core::target::Item, + ) -> jetted_core::Result> { + let result = match item { + jetted_core::target::Item::Auxiliary { .. } => None, + jetted_core::target::Item::Preamble => { + writeln!(out, " None, + jetted_core::target::Item::Struct { + metadata, + name, + has_additional, + fields, + } => { + writeln!(out, "final class {name} {{")?; + for field in &fields { + writeln!(out, " private ${};", field.name)? + } + write!(out, "\n")?; + codegen::generate_constructor(out, 1, &fields)?; + codegen::generate_serializer(out, 1, &fields)?; + codegen::generate_deserializer(out, 1, &fields)?; + codegen::generate_getters(out, 1, &fields)?; + + writeln!(out, "}}")?; + None + } + + _ => None, + /* + jetted_core::target::Item::Alias { + metadata, + name, + type_, + } => todo!(), + jetted_core::target::Item::Enum { + metadata, + name, + members, + } => todo!(), + jetted_core::target::Item::Discriminator { + metadata, + name, + tag_field_name, + tag_json_name, + variants, + } => todo!(), + jetted_core::target::Item::DiscriminatorVariant { + metadata, + name, + parent_name, + tag_field_name, + tag_json_name, + tag_value, + has_additional, + fields, + } => todo!(), + */ + }; + Ok(result) + } +} + +#[cfg(test)] +mod tests { + mod std_tests { + jetted_test::std_test_cases!(&crate::Target::new(None)); + } + + mod optional_std_tests { + jetted_test::strict_std_test_case!( + &crate::Target::new(None), + empty_and_nonascii_properties + ); + + jetted_test::strict_std_test_case!( + &crate::Target::new(None), + empty_and_nonascii_enum_values + ); + } +}