diff --git a/README.md b/README.md index 65c0659..2c5776e 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ Another serialization system: minimalist and canonicalization. - [Encoding Spec](docs/encoding_spec.md) - [Schema Language](docs/schema_language.md) - [Real-World Examples](docs/real_world_examples.md) +- [API](docs/molecule_api.md) ## Features * `default` — Default features: `std`, utilizes `faster-hex` for hexadecimal operations and enables [bytes] standard features. diff --git a/docs/molecule_api.md b/docs/molecule_api.md new file mode 100644 index 0000000..acbb0fb --- /dev/null +++ b/docs/molecule_api.md @@ -0,0 +1,85 @@ +## Molecule API + +Molecule is based on schema serialization. The serialization format generated by any language based on the same schema should be consistent. The most basic API for multiple languages ​​should include data verification, schema compatibility reading, serialization and deserialization, and obtaining raw data (excluding the molecule header). + +### Rust API + +In the basic implementation of rust, the generated code uses three traits to implement three different functions of a structure: + +#### Entity +```rust +pub trait Entity: fmt::Debug + Default + Clone { + type Builder: Builder; + const NAME: &'static str; + fn new_unchecked(data: Bytes) -> Self; + fn as_bytes(&self) -> Bytes; + fn as_slice(&self) -> &[u8]; + fn from_slice(slice: &[u8]) -> VerificationResult; + fn from_compatible_slice(slice: &[u8]) -> VerificationResult; + fn new_builder() -> Self::Builder; + fn as_builder(self) -> Self::Builder; +} +``` +Entity corresponds to a serialized data structure. There are two APIs that are easily confused: `as_slice` defined in the trait and `raw_data` that comes with its own structure. + +`as_slice`: Complete molecule format data +`raw_data`: Original data, i.e. without the molecule header + +Molecule supports compatible reading of data. The so-called compatibility refers to a structure like table, which can dynamically add fields. The old schema can read the data generated by the schema after adding fields, but it does not support deleting fields. + +```mol +vector Inner ; + +table Old { + a: Inner, + b: Inner, +} + +table New { + a: Inner, + b: Inner, + c: Inner, +} +``` + +Like the scheme in the example above, `Old` can use the `from_compatible_slice` api to read `New's` data.At the same time, the structure will have APIs such as `count_extra_fields`/`has_extra_fields`/`has_extra_fields` to let users know that there is extra data in the read data. It is currently compatible with reading. + +#### Reader +```rust +pub trait Reader<'r>: Sized + fmt::Debug + Clone + Copy { + type Entity: Entity; + const NAME: &'static str; + fn verify(slice: &[u8], compatible: bool) -> VerificationResult<()>; + fn new_unchecked(slice: &'r [u8]) -> Self; + fn as_slice(&self) -> &'r [u8]; + fn from_slice(slice: &'r [u8]) -> VerificationResult { + Self::verify(slice, false).map(|_| Self::new_unchecked(slice)) + } + fn from_compatible_slice(slice: &'r [u8]) -> VerificationResult { + Self::verify(slice, true).map(|_| Self::new_unchecked(slice)) + } + fn to_entity(&self) -> Self::Entity; +} +``` + +Each structure will generate at least one corresponding `Reader` structure, which has the ability to obtain the field data inside the structure. + + +#### Builder + +```rust +pub trait Builder: Default { + type Entity: Entity; + const NAME: &'static str; + fn expected_length(&self) -> usize; + fn write(&self, writer: &mut W) -> io::Result<()>; + fn build(&self) -> Self::Entity; +} +``` + +Builder is the key to building and serializing the molecule structure. Use the builder mode to generate a builder structure, put all fields into it, and convert it into a serialized structure(Entity) through the `build` API. + + +#### Union + +The union structure will have two more structures than other structures when generating code, corresponding to different internal data types, and ends with Union. diff --git a/examples/ci-tests/src/simple-example.rs b/examples/ci-tests/src/simple-example.rs index cb3a7c2..2166b25 100644 --- a/examples/ci-tests/src/simple-example.rs +++ b/examples/ci-tests/src/simple-example.rs @@ -15,17 +15,14 @@ fn display_test_data() { let f0: types::Byte = 0x12u8.into(); let f2 = types::Byte3::new_builder().nth1(f0).build(); let f29 = types::StructB::new_builder() - .f2(0x34u8.into()) + .f2(0x34u8) .f4(f2.clone()) .build(); let f41 = { - let mut f41_builder = types::Bytes::new_builder() - .push(0x12.into()) - .push(0x12.into()) - .push(0x13.into()); - assert_eq!(f41_builder.replace(1, 0x34.into()), Some(0x12.into())); - assert_eq!(f41_builder.replace(2, 0x56.into()), Some(0x13.into())); - assert_eq!(f41_builder.replace(3, 0x56.into()), None); + let mut f41_builder = types::Bytes::new_builder().push(0x12).push(0x12).push(0x13); + assert_eq!(f41_builder.replace(1, 0x34), Some(0x12.into())); + assert_eq!(f41_builder.replace(2, 0x56), Some(0x13.into())); + assert_eq!(f41_builder.replace(3, 0x56), None); f41_builder.build() }; let f43 = types::Byte3Vec::new_builder() diff --git a/examples/ci-tests/tests/proptest.rs b/examples/ci-tests/tests/proptest.rs index 32ceaf0..3614fe0 100644 --- a/examples/ci-tests/tests/proptest.rs +++ b/examples/ci-tests/tests/proptest.rs @@ -52,7 +52,7 @@ fn arbitrary_table0() -> impl Strategy { } fn arbitrary_table1() -> impl Strategy { - any::().prop_map(|data| types::Table1::new_builder().f1(data.into()).build()) + any::().prop_map(|data| types::Table1::new_builder().f1(data).build()) } fn arbitrary_table5() -> impl Strategy { diff --git a/examples/ci-tests/tests/simple.rs b/examples/ci-tests/tests/simple.rs index 514ab93..000db56 100644 --- a/examples/ci-tests/tests/simple.rs +++ b/examples/ci-tests/tests/simple.rs @@ -45,15 +45,15 @@ macro_rules! test_option_set_default { } macro_rules! test_vector_push_default { - ($type:ident, $expected1:expr, $expected2:expr, $expected3:expr) => { + ($type:ident, $item:ident, $expected1:expr, $expected2:expr, $expected3:expr) => { let t = types::$type::default(); - let t = test_vector_push_default!($type, t, $expected1); - let t = test_vector_push_default!($type, t, $expected2); - let _ = test_vector_push_default!($type, t, $expected3); + let t = test_vector_push_default!($type, $item, t, $expected1); + let t = test_vector_push_default!($type, $item, t, $expected2); + let _ = test_vector_push_default!($type, $item, t, $expected3); }; - ($type:ident, $input:ident, $expected:expr) => {{ + ($type:ident, $item:ident, $input:ident, $expected:expr) => {{ let expected = $expected; - let builder = $input.as_builder().push(Default::default()); + let builder = $input.as_builder().push(types::$item::default()); let result = builder.build(); assert_eq!( result.as_slice(), @@ -426,6 +426,7 @@ fn option_set_default() { fn fixvec_push_default() { test_vector_push_default!( Bytes, + Byte, s!("0x\ 01000000\ 00\ @@ -444,6 +445,7 @@ fn fixvec_push_default() { ); test_vector_push_default!( Words, + Word, s!("0x\ 01000000\ 0000\ @@ -462,6 +464,7 @@ fn fixvec_push_default() { ); test_vector_push_default!( Byte3Vec, + Byte3, s!("0x\ 01000000\ 000000\ @@ -480,6 +483,7 @@ fn fixvec_push_default() { ); test_vector_push_default!( Byte7Vec, + Byte7, s!("0x\ 01000000\ 00000000_000000\ @@ -498,6 +502,7 @@ fn fixvec_push_default() { ); test_vector_push_default!( StructIVec, + StructI, s!("0x\ 01000000\ 00000000\ @@ -516,6 +521,7 @@ fn fixvec_push_default() { ); test_vector_push_default!( StructJVec, + StructJ, s!("0x\ 01000000\ 00000000_000000\ @@ -534,6 +540,7 @@ fn fixvec_push_default() { ); test_vector_push_default!( StructPVec, + StructP, s!("0x\ 01000000\ 00000000_00000000\ @@ -581,8 +588,8 @@ fn dynvec_push_default() { 00000000\ 00000000\ "); - test_vector_push_default!(BytesVec, s1, s2, s3); - test_vector_push_default!(WordsVec, s1, s2, s3); + test_vector_push_default!(BytesVec, Bytes, s1, s2, s3); + test_vector_push_default!(WordsVec, Words, s1, s2, s3); let s1 = s!("0x\ 08000000\ \ @@ -601,8 +608,8 @@ fn dynvec_push_default() { 10000000\ 10000000\ "); - test_vector_push_default!(ByteOptVec, s1, s2, s3); - test_vector_push_default!(WordOptVec, s1, s2, s3); - test_vector_push_default!(WordsOptVec, s1, s2, s3); - test_vector_push_default!(BytesOptVec, s1, s2, s3); + test_vector_push_default!(ByteOptVec, ByteOpt, s1, s2, s3); + test_vector_push_default!(WordOptVec, WordOpt, s1, s2, s3); + test_vector_push_default!(WordsOptVec, WordsOpt, s1, s2, s3); + test_vector_push_default!(BytesOptVec, BytesOpt, s1, s2, s3); } diff --git a/tools/codegen/src/generator/languages/rust/builder/implementation.rs b/tools/codegen/src/generator/languages/rust/builder/implementation.rs index e396940..f7a138e 100644 --- a/tools/codegen/src/generator/languages/rust/builder/implementation.rs +++ b/tools/codegen/src/generator/languages/rust/builder/implementation.rs @@ -278,6 +278,12 @@ fn gen_from_iter(name: &str, item_name: &str) -> m4::TokenStream { Self::new_builder().extend(iter.into_iter().map(Into::into)).build() } } + + impl From> for #entity { + fn from(v: Vec) -> Self { + Self::new_builder().set(v.into_iter().map(Into::into).collect()).build() + } + } ) } else { quote!() @@ -290,6 +296,13 @@ fn gen_from_iter(name: &str, item_name: &str) -> m4::TokenStream { } } + impl From> for #entity + { + fn from(v: Vec<#item_name>) -> Self { + Self::new_builder().set(v).build() + } + } + #maybe_byte_vec ) } diff --git a/tools/codegen/src/generator/languages/rust/builder/setters.rs b/tools/codegen/src/generator/languages/rust/builder/setters.rs index 4d32e86..45fb28a 100644 --- a/tools/codegen/src/generator/languages/rust/builder/setters.rs +++ b/tools/codegen/src/generator/languages/rust/builder/setters.rs @@ -12,8 +12,11 @@ impl ImplSetters for ast::Option_ { fn impl_setters(&self) -> m4::TokenStream { let inner = entity_name(self.item().typ().name()); quote!( - pub fn set(mut self, v: Option<#inner>) -> Self { - self.0 = v; + pub fn set(mut self, v: T) -> Self + where + T: ::core::convert::Into> + { + self.0 = v.into(); self } ) @@ -40,8 +43,11 @@ impl ImplSetters for ast::Array { let inner = entity_name(self.item().typ().name()); let item_count = usize_lit(self.item_count()); let entire_setter = quote!( - pub fn set(mut self, v: [#inner; #item_count]) -> Self { - self.0 = v; + pub fn set(mut self, v: T) -> Self + where + T: ::core::convert::Into<[#inner; #item_count]> + { + self.0 = v.into(); self } ); @@ -50,8 +56,11 @@ impl ImplSetters for ast::Array { let index = usize_lit(idx); let func = func_name(&format!("nth{}", idx)); quote!( - pub fn #func(mut self, v: #inner) -> Self { - self.0[#index] = v; + pub fn #func(mut self, v: T) -> Self + where + T: ::core::convert::Into<#inner> + { + self.0[#index] = v.into(); self } ) @@ -95,8 +104,11 @@ fn impl_setters_for_struct_or_table(inner: &[ast::FieldDecl]) -> m4::TokenStream let field_name = field_name(f.name()); let field_type = entity_name(f.typ().name()); quote!( - pub fn #field_name(mut self, v: #field_type) -> Self { - self.#field_name = v; + pub fn #field_name(mut self, v: T) -> Self + where + T: ::core::convert::Into<#field_type> + { + self.#field_name = v.into(); self } ) @@ -110,22 +122,27 @@ fn impl_setters_for_struct_or_table(inner: &[ast::FieldDecl]) -> m4::TokenStream fn impl_setters_for_vector(inner_name: &str) -> m4::TokenStream { let inner = entity_name(inner_name); quote!( - pub fn set(mut self, v: Vec<#inner>) -> Self { + pub fn set(mut self, v: Vec<#inner>) -> Self + { self.0 = v; self } - pub fn push(mut self, v: #inner) -> Self { - self.0.push(v); + pub fn push(mut self, v: T) -> Self + where + T: ::core::convert::Into<#inner> + { + self.0.push(v.into()); self } pub fn extend>(mut self, iter: T) -> Self { - for elem in iter { - self.0.push(elem); - } + self.0.extend(iter); self } - pub fn replace(&mut self, index: usize, v: #inner) -> Option<#inner> { - self.0.get_mut(index).map(|item| ::core::mem::replace(item, v)) + pub fn replace(&mut self, index: usize, v: T) -> Option<#inner> + where + T: ::core::convert::Into<#inner> + { + self.0.get_mut(index).map(|item| ::core::mem::replace(item, v.into())) } ) }