Skip to content

Commit

Permalink
docs: add rust api doc
Browse files Browse the repository at this point in the history
  • Loading branch information
driftluo committed Jul 8, 2024
1 parent 4988418 commit dd1736e
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 3 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
85 changes: 85 additions & 0 deletions docs/molecule_api.md
Original file line number Diff line number Diff line change
@@ -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<Self>;
fn from_compatible_slice(slice: &[u8]) -> VerificationResult<Self>;
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 <byte>;
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> {
Self::verify(slice, false).map(|_| Self::new_unchecked(slice))
}
fn from_compatible_slice(slice: &'r [u8]) -> VerificationResult<Self> {
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<W: io::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.
Original file line number Diff line number Diff line change
Expand Up @@ -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<Vec<u8>> for #entity {
fn from(v: Vec<u8>) -> Self {
Self::new_builder().set(v.into_iter().map(Into::into).collect()).build()
}
}
)
} else {
quote!()
Expand All @@ -290,6 +296,13 @@ fn gen_from_iter(name: &str, item_name: &str) -> m4::TokenStream {
}
}

impl From<Vec<#item_name>> for #entity
{
fn from(v: Vec<#item_name>) -> Self {
Self::new_builder().set(v).build()
}
}

#maybe_byte_vec
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,7 @@ fn impl_setters_for_vector(inner_name: &str) -> m4::TokenStream {
self
}
pub fn extend<T: ::core::iter::IntoIterator<Item=#inner>>(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> {
Expand Down

0 comments on commit dd1736e

Please sign in to comment.