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).
In the basic implementation of rust, the generated code uses three traits to implement three different functions of a structure:
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.
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.
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.
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.
The union structure will have two more structures than other structures when generating code, corresponding to different internal data types, and ends with Union.