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/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..851a025 100644 --- a/tools/codegen/src/generator/languages/rust/builder/setters.rs +++ b/tools/codegen/src/generator/languages/rust/builder/setters.rs @@ -119,9 +119,7 @@ fn impl_setters_for_vector(inner_name: &str) -> m4::TokenStream { 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> {