diff options
| author | Michael Williamson <mike@zwobble.org> | 2026-07-31 10:57:39 +0100 |
|---|---|---|
| committer | Michael Williamson <mike@zwobble.org> | 2026-07-31 10:57:39 +0100 |
| commit | 2f76934bf2aa59f78b671c722b73f0d69c7adbd3 (patch) | |
| tree | 6784f78eafe1bd2d906f410440b101b660e09077 | |
| parent | 578bf1f04b48f48225cfa794a27426cca4f6384a (diff) | |
Handle singleton structs in Rust
12 files changed, 86 insertions, 40 deletions
diff --git a/examples/02-struct-singleton/output/rust/src/point.rs b/examples/02-struct-singleton/output/rust/src/point.rs index e2d608c..866f0c5 100644 --- a/examples/02-struct-singleton/output/rust/src/point.rs +++ b/examples/02-struct-singleton/output/rust/src/point.rs @@ -1,5 +1,4 @@ // Generated by hobgoblin. #[derive(Debug, Hash, PartialEq)] -pub struct Origin { -} +pub struct Origin; diff --git a/examples/12-transient-0/output/java/src/test/java/org/zwobble/example/Transient0Tests.java b/examples/12-transient-0/output/java/src/test/java/org/zwobble/example/Transient0Tests.java index a924d1e..d8d4f3f 100644 --- a/examples/12-transient-0/output/java/src/test/java/org/zwobble/example/Transient0Tests.java +++ b/examples/12-transient-0/output/java/src/test/java/org/zwobble/example/Transient0Tests.java @@ -15,6 +15,7 @@ import java.util.Optional; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.zwobble.example.types.data.EnumWithVariants; +import org.zwobble.example.types.data.StructSingleton; import org.zwobble.example.types.data.StructWithBool; import org.zwobble.example.types.data.StructWithDifferentSharedTypes; import org.zwobble.example.types.data.StructWithEnum; @@ -34,6 +35,18 @@ import org.zwobble.example.types.transient0.HobgoblinTransient0TaggedSharedValue public class Transient0Tests { @Test + public void structSingleton() throws IOException { + var value = StructSingleton.INSTANCE; + + assertRoundTripEncoding( + "StructSingleton", + value, + HobgoblinTransient0Data::encodeStructSingleton, + HobgoblinTransient0Data::decodeStructSingleton + ); + } + + @Test public void structWithBool() throws IOException { var value = new StructWithBool(true, false); diff --git a/examples/12-transient-0/output/rust/src/gen/data.rs b/examples/12-transient-0/output/rust/src/gen/data.rs index 4ec0524..aef9f69 100644 --- a/examples/12-transient-0/output/rust/src/gen/data.rs +++ b/examples/12-transient-0/output/rust/src/gen/data.rs @@ -1,6 +1,9 @@ // Generated by hobgoblin. #[derive(Debug, Hash, PartialEq)] +pub struct StructSingleton; + +#[derive(Debug, Hash, PartialEq)] pub struct StructWithBool { pub a: ::core::primitive::bool, pub b: ::core::primitive::bool, diff --git a/examples/12-transient-0/output/rust/src/gen/data/transient_0.rs b/examples/12-transient-0/output/rust/src/gen/data/transient_0.rs index ea4a459..ca9beaf 100644 --- a/examples/12-transient-0/output/rust/src/gen/data/transient_0.rs +++ b/examples/12-transient-0/output/rust/src/gen/data/transient_0.rs @@ -1,5 +1,13 @@ // Generated by hobgoblin. +pub fn encode_struct_singleton(value: &crate::data::StructSingleton, writer: &mut impl std::io::Write, shared_values: &mut ::std::collections::HashMap::<::core::primitive::usize, ::core::primitive::i64>) -> ::std::io::Result::<()> { + ::std::io::Result::Ok(()) +} + +pub fn decode_struct_singleton(reader: &mut impl std::io::Read, shared_values: &mut ::std::vec::Vec::<::std::sync::Arc::<dyn ::std::any::Any + ::std::marker::Sync + ::std::marker::Send>>) -> ::std::io::Result::<crate::data::StructSingleton> { + ::std::io::Result::Ok(crate::data::StructSingleton) +} + pub fn encode_struct_with_bool(value: &crate::data::StructWithBool, writer: &mut impl std::io::Write, shared_values: &mut ::std::collections::HashMap::<::core::primitive::usize, ::core::primitive::i64>) -> ::std::io::Result::<()> { crate::transient_0::encode_bool(&value.a, writer, shared_values)?; crate::transient_0::encode_bool(&value.b, writer, shared_values)?; diff --git a/examples/12-transient-0/output/rust/src/lib.rs b/examples/12-transient-0/output/rust/src/lib.rs index 3a02936..365a352 100644 --- a/examples/12-transient-0/output/rust/src/lib.rs +++ b/examples/12-transient-0/output/rust/src/lib.rs @@ -8,7 +8,19 @@ mod test { use std::path::PathBuf; use std::sync::Arc; use std::collections::HashMap; - use super::data::{EnumWithVariants, StructWithBool, StructWithDifferentSharedTypes, StructWithEnum, StructWithInt32, StructWithInt64, StructWithList, StructWithListOfShared, StructWithOption, StructWithSharedSumAndVariant, StructWithString, StructWithStruct, StructWithSum, SumWithVariants, VariantOne, VariantTwo }; + use super::data::{EnumWithVariants, StructSingleton, StructWithBool, StructWithDifferentSharedTypes, StructWithEnum, StructWithInt32, StructWithInt64, StructWithList, StructWithListOfShared, StructWithOption, StructWithSharedSumAndVariant, StructWithString, StructWithStruct, StructWithSum, SumWithVariants, VariantOne, VariantTwo }; + + #[test] + fn struct_singleton() { + let value = StructSingleton; + + assert_round_trip_encoding( + "StructSingleton", + value, + super::data::transient_0::encode_struct_singleton, + super::data::transient_0::decode_struct_singleton, + ); + } #[test] fn struct_with_bool() { diff --git a/examples/12-transient-0/src/data.hob b/examples/12-transient-0/src/data.hob index 9c715e9..0a55e39 100644 --- a/examples/12-transient-0/src/data.hob +++ b/examples/12-transient-0/src/data.hob @@ -1,3 +1,7 @@ +struct StructSingleton { + singleton; +} + struct StructWithBool { field a: Bool; field b: Bool; diff --git a/hobgoblin/src/output/lang/rust/ast.hob b/hobgoblin/src/output/lang/rust/ast.hob index f1a4513..1fbfa99 100644 --- a/hobgoblin/src/output/lang/rust/ast.hob +++ b/hobgoblin/src/output/lang/rust/ast.hob @@ -37,7 +37,7 @@ struct RustFunctionParam { struct RustStructStruct { field attributes: List[RustOuterAttribute]; field name: RustIdentifier; - field fields: List[RustStructField]; + field fields: Option[List[RustStructField]]; field docComment: DocComment; } diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/rusttypes/RustTypesGenerator.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/rusttypes/RustTypesGenerator.java index 8ed9d9e..39a0b8b 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/rusttypes/RustTypesGenerator.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/rusttypes/RustTypesGenerator.java @@ -127,12 +127,14 @@ public class RustTypesGenerator implements Generator { var rustStructName = this.rustGenerator.generateTypeName(structDefinition.name()); - var rustFields = structDefinition.fields().orElse(List.of()).stream() - .map(field -> new RustStructField( - this.rustGenerator.generateFieldName(field.name()), - generateRustTypeExpression(field.type(), context) - )) - .toList(); + var rustFields = structDefinition.fields().map( + fields -> fields.stream() + .map(field -> new RustStructField( + this.rustGenerator.generateFieldName(field.name()), + generateRustTypeExpression(field.type(), context) + )) + .toList() + ); var rustStruct = new RustStructStruct( rustAttributes, diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/rust/RustWriter.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/rust/RustWriter.java index d0552ad..668cd75 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/rust/RustWriter.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/rust/RustWriter.java @@ -136,12 +136,14 @@ public class RustWriter implements AutoCloseable { this.writerOuterAttributes(structStruct.attributes()); this.writer.write("pub struct "); this.writeIdentifier(structStruct.name()); - this.writer.write(" {"); - if (!structStruct.fields().isEmpty()) { + if (structStruct.fields().isEmpty()) { + this.writer.write(";"); + } else { + this.writer.write(" {"); this.writer.indent(); - for (var field : structStruct.fields()) { + for (var field : structStruct.fields().get()) { this.writer.newLine(); this.writer.write("pub "); this.writeIdentifier(field.name()); @@ -151,10 +153,9 @@ public class RustWriter implements AutoCloseable { } this.writer.dedent(); + this.writer.newLine(); + this.writer.write("}"); } - - this.writer.newLine(); - this.writer.write("}"); } // === Enumerations === diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/rust/ast/RustStructStruct.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/rust/ast/RustStructStruct.java index 4bc3a1e..2838a68 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/rust/ast/RustStructStruct.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/rust/ast/RustStructStruct.java @@ -8,17 +8,17 @@ package org.zwobble.hobgoblin.compiler.output.lang.rust.ast; public record RustStructStruct( java.util.List<org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustOuterAttribute> attributes, org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustIdentifier name, - java.util.List<org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustStructField> fields, + java.util.Optional<java.util.List<org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustStructField>> fields, org.zwobble.hobgoblin.compiler.ast.DocComment docComment ) implements org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustItem { public static org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustStructStruct.Builder arbitrary() { - return new org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustStructStruct.Builder(java.util.List.of(), org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustIdentifier.arbitrary().build(), java.util.List.of(), org.zwobble.hobgoblin.compiler.ast.HobgoblinNativeAst.arbitraryDocComment()); + return new org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustStructStruct.Builder(java.util.List.of(), org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustIdentifier.arbitrary().build(), java.util.Optional.empty(), org.zwobble.hobgoblin.compiler.ast.HobgoblinNativeAst.arbitraryDocComment()); } public record Builder( java.util.List<org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustOuterAttribute> attributes, org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustIdentifier name, - java.util.List<org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustStructField> fields, + java.util.Optional<java.util.List<org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustStructField>> fields, org.zwobble.hobgoblin.compiler.ast.DocComment docComment ) implements org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustItem.Builder { public org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustStructStruct build() { @@ -49,20 +49,12 @@ public record RustStructStruct( return new org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustStructStruct.Builder(attributes, name.build(), fields, docComment); } - public org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustStructStruct.Builder withFields(java.util.List<org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustStructField> fields) { - return new org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustStructStruct.Builder(attributes, name, fields, docComment); - } - - public org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustStructStruct.Builder addField(org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustStructField field) { - var fields = new java.util.ArrayList<org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustStructField>(this.fields); - fields.add(field); + public org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustStructStruct.Builder withFields(java.util.Optional<java.util.List<org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustStructField>> fields) { return new org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustStructStruct.Builder(attributes, name, fields, docComment); } - public org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustStructStruct.Builder addField(org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustStructField.Builder field) { - var fields = new java.util.ArrayList<org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustStructField>(this.fields); - fields.add(field.build()); - return new org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustStructStruct.Builder(attributes, name, fields, docComment); + public org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustStructStruct.Builder withFields(java.util.List<org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustStructField> fields) { + return new org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustStructStruct.Builder(attributes, name, java.util.Optional.of(fields), docComment); } public org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustStructStruct.Builder withDocComment(org.zwobble.hobgoblin.compiler.ast.DocComment docComment) { diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/output/lang/rust/RustWriterTests.java b/src/test/java/org/zwobble/hobgoblin/compiler/output/lang/rust/RustWriterTests.java index 4a81031..56d6d87 100644 --- a/src/test/java/org/zwobble/hobgoblin/compiler/output/lang/rust/RustWriterTests.java +++ b/src/test/java/org/zwobble/hobgoblin/compiler/output/lang/rust/RustWriterTests.java @@ -28,11 +28,9 @@ public class RustWriterTests { assertThat(string, equalTo(""" // Generated by hobgoblin. - pub struct Rectangle { - } + pub struct Rectangle; - pub struct Square { - } + pub struct Square; """)); } @@ -112,8 +110,24 @@ public class RustWriterTests { // === Structs === @Test + public void unitLikeStruct() throws IOException { + var rust = RustStructStruct.arbitrary() + .withName(new RustIdentifier("Rectangle")) + .withFields(Optional.empty()) + .build(); + + var string = write(writer -> writer.writeItem(rust)); + + assertThat(string, equalTo(""" + pub struct Rectangle;""")); + } + + @Test public void emptyStruct() throws IOException { - var rust = RustStructStruct.arbitrary().withName(new RustIdentifier("Rectangle")).build(); + var rust = RustStructStruct.arbitrary() + .withName(new RustIdentifier("Rectangle")) + .withFields(List.of()) + .build(); var string = write(writer -> writer.writeItem(rust)); @@ -137,8 +151,7 @@ public class RustWriterTests { assertThat(string, equalTo(""" #[a::b::C] #[derive(Debug)] - pub struct Rectangle { - }""")); + pub struct Rectangle;""")); } @Test @@ -171,8 +184,7 @@ public class RustWriterTests { assertThat(string, equalTo(""" /// A 2D rectangle. - pub struct Rectangle { - }""")); + pub struct Rectangle;""")); } // === Enumerations === diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/output/lang/rust/ast/RustStructStructMatcher.java b/src/test/java/org/zwobble/hobgoblin/compiler/output/lang/rust/ast/RustStructStructMatcher.java index b2109c1..6f04455 100644 --- a/src/test/java/org/zwobble/hobgoblin/compiler/output/lang/rust/ast/RustStructStructMatcher.java +++ b/src/test/java/org/zwobble/hobgoblin/compiler/output/lang/rust/ast/RustStructStructMatcher.java @@ -42,7 +42,7 @@ public class RustStructStructMatcher implements org.zwobble.precisely.Matcher<ja return new org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustStructStructMatcher(submatchers); } - public org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustStructStructMatcher withFields(org.zwobble.precisely.Matcher<? super java.util.List<org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustStructField>> fields) { + public org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustStructStructMatcher withFields(org.zwobble.precisely.Matcher<? super java.util.Optional<java.util.List<org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustStructField>>> fields) { var submatchers = new java.util.ArrayList<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustStructStruct>>(this.submatchers); submatchers.add(org.zwobble.precisely.Matchers.has("fields", org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustStructStruct::fields, fields)); return new org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustStructStructMatcher(submatchers); |
