| Crates.io | shapely |
| lib.rs | shapely |
| version | 3.1.0 |
| created_at | 2025-03-10 20:30:02.555549+00 |
| updated_at | 2025-03-31 19:07:44.630542+00 |
| description | One trait for reflection, serialization, deserialization |
| homepage | https://github.com/bearcove/shapely |
| repository | https://github.com/bearcove/shapely |
| max_upload_size | |
| id | 1587206 |
| size | 19,278 |
shapely provides runtime reflection for Rust.
Any type that implements Shapely trait returns a Shape, which describes:
The Partial type is able to allocate (or work from a &mut MaybeUninit<T>)
any Shapely type, and gradually initialize its fields — until the fully-built
value is moved out of the partial.
It comes with a derive macro that uses unsynn for speed of compilation.
The main shapely crate re-exports symbols from:
Shapely trait and the Shape structShapely derive attribute as a fast/light proc macro powered by unsynnshapely supports deserialization from multiple data formats through dedicated crates:
Additionally:
shapely-coreTo implement a custom deserializer for a new format, you'll need to work with the following key components from shapely:
Partial]: The central type for building shapely values incrementallyShape]: Describes the memory layout and structure of a typeInnards]: Represents the internal structure (Scalar, Struct, etc.)Scalar]: Represents primitive types like String, u64, etc.&mut [Partial] and your format's input (string, bytes, etc.)Partial::shape]Shape::innards]:
Innards::Scalar], use [Partial::scalar_slot] to get and fill the slotInnards::Struct], iterate through fields, using [Partial::slot_by_name] to access each fieldInnards::Map] and [Innards::List] come with vtables, they have helper slots as wellPartial] instances for complex fields and fill them recursivelyWhen in doubt, refer to the shapely-json implementation — it's the most featureful.
use shapely_core::{Partial, Innards, Scalar, FieldError, Shape};
use std::convert::From;
#[derive(Debug)]
pub enum MyFormatError {
UnsupportedType,
UnsupportedShape,
FieldError(FieldError),
}
impl From<FieldError> for MyFormatError {
fn from(err: FieldError) -> Self {
MyFormatError::FieldError(err)
}
}
pub fn from_my_format(partial: &mut Partial, input: &[u8]) -> Result<(), MyFormatError> {
let shape_desc = partial.shape();
let shape = shape_desc.get();
match &shape.innards {
Innards::Scalar(scalar) => {
let slot = partial.scalar_slot().expect("Scalar slot");
// Parse scalar value from input and fill slot
match scalar {
Scalar::String => slot.fill("/* parsed string */".to_string()),
Scalar::U64 => slot.fill(0u64),
// Handle other scalar types
_ => return Err(MyFormatError::UnsupportedType),
}
},
Innards::Struct { .. } => {
// Parse struct fields from input
for (field_name, field_value) in [("field1", "value1"), ("field2", "value2")].iter() {
let slot = partial.slot_by_name(field_name)?;
// Create a partial for the field and fill it recursively
let mut partial_field = Partial::alloc(slot.shape());
// Recursively deserialize field_value into partial_field
// ...
slot.fill_from_partial(partial_field);
}
},
// Handle other shapes as needed
_ => return Err(MyFormatError::UnsupportedShape),
}
Ok(())
}
For more detailed examples, examine the implementation of existing deserializers like shapely-json or shapely-msgpack.
Thanks to Namespace for providing fast GitHub Actions workers:
Licensed under either of:
at your option.