mirror of
https://github.com/trussed-dev/serde-indexed.git
synced 2026-06-20 04:16:32 -07:00
Minor changes
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "serde-indexed"
|
||||
version = "0.0.1"
|
||||
version = "0.0.2"
|
||||
authors = ["Nicolas Stalder <n@stalder.io>"]
|
||||
license = "Apache-2.0 OR MIT"
|
||||
description = "Derivation of Serialize and Deserialize that replaces struct keys with numerical indices."
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
## serde-indexed
|
||||
|
||||
[![crates.io][crates-image]][crates-link]
|
||||
[![Documentation][docs-image]][docs-link]
|
||||
|
||||
Derivation of [`Serialize`][serialize] and [`Deserialize`][deserialize] that replaces struct keys with numerical indices.
|
||||
|
||||
Primary use case is to handle [CTAP CBOR][ctap-cbor] messages, in particular support for:
|
||||
@@ -23,6 +26,10 @@ This was a nice opportunity to learn proc-macros, I roughly followed [`serde-rep
|
||||
|
||||
To see some generated code, run `cargo expand --test basics`.
|
||||
|
||||
[crates-image]: https://img.shields.io/crates/v/serde-indexed.svg?style=flat-square
|
||||
[crates-link]: https://crates.io/crates/serde-indexed
|
||||
[docs-image]: https://img.shields.io/badge/docs.rs-api-green?style=flat-square
|
||||
[docs-link]: https://docs.rs/serde-indexed
|
||||
[serialize]: https://docs.serde.rs/serde/ser/trait.Serialize.html
|
||||
[deserialize]: https://docs.serde.rs/serde/de/trait.Deserialize.html
|
||||
[ctap-cbor]: https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#ctap2-canonical-cbor-encoding-form
|
||||
|
||||
+9
-11
@@ -1,7 +1,7 @@
|
||||
/*! Derivation of [`Serialize`][serialize] and [`Deserialize`][deserialize] that replaces struct keys with numerical indices.
|
||||
|
||||
### Usage example
|
||||
The macros currently understand `serde`'s [`skip_if_serialized`][skip-serializing-if] field attribute
|
||||
The macros currently understand `serde`'s [`skip_serializing_if`][skip-serializing-if] field attribute
|
||||
and a custom `offset` container attribute.
|
||||
|
||||
```
|
||||
@@ -31,7 +31,7 @@ extern crate proc_macro;
|
||||
mod parse;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use quote::{format_ident, quote};
|
||||
use syn::parse_macro_input;
|
||||
|
||||
use crate::parse::Input;
|
||||
@@ -69,10 +69,10 @@ fn count_serialized_fields(fields: &[parse::Field]) -> Vec<proc_macro2::TokenStr
|
||||
let member = &field.member;
|
||||
match &field.skip_serializing_if {
|
||||
Some(path) => {
|
||||
quote! { + if #path(&self.#member) { 0 } else { 1 } }
|
||||
quote! { if #path(&self.#member) { 0 } else { 1 } }
|
||||
}
|
||||
None => {
|
||||
quote! { + 1 }
|
||||
quote! { 1 }
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -93,9 +93,7 @@ pub fn derive_serialize(input: TokenStream) -> TokenStream {
|
||||
S: serde::Serializer
|
||||
{
|
||||
use serde::ser::SerializeMap;
|
||||
let num_fields = 0
|
||||
#(#num_fields)*
|
||||
;
|
||||
let num_fields = 0 #( + #num_fields)*;
|
||||
let mut map = serializer.serialize_map(Some(num_fields))?;
|
||||
|
||||
#(#serialize_fields)*
|
||||
@@ -110,7 +108,7 @@ fn none_fields(fields: &[parse::Field]) -> Vec<proc_macro2::TokenStream> {
|
||||
fields
|
||||
.iter()
|
||||
.map(|field| {
|
||||
let ident = syn::Ident::new(&field.label, proc_macro2::Span::call_site());
|
||||
let ident = format_ident!("{}", &field.label);
|
||||
quote! {
|
||||
let mut #ident = None;
|
||||
}
|
||||
@@ -123,7 +121,7 @@ fn unwrap_expected_fields(fields: &[parse::Field]) -> Vec<proc_macro2::TokenStre
|
||||
.iter()
|
||||
.map(|field| {
|
||||
let label = field.label.clone();
|
||||
let ident = syn::Ident::new(&field.label, proc_macro2::Span::call_site());
|
||||
let ident = format_ident!("{}", &field.label);
|
||||
if field.skip_serializing_if.is_none() {
|
||||
quote! {
|
||||
let #ident = #ident.ok_or_else(|| serde::de::Error::missing_field(#label))?;
|
||||
@@ -143,7 +141,7 @@ fn match_fields(fields: &[parse::Field], offset: usize) -> Vec<proc_macro2::Toke
|
||||
.iter()
|
||||
.map(|field| {
|
||||
let label = field.label.clone();
|
||||
let ident = syn::Ident::new(&field.label, proc_macro2::Span::call_site());
|
||||
let ident = format_ident!("{}", &field.label);
|
||||
let index = field.index + offset;
|
||||
quote! {
|
||||
#index => {
|
||||
@@ -161,7 +159,7 @@ fn all_fields(fields: &[parse::Field]) -> Vec<proc_macro2::TokenStream> {
|
||||
fields
|
||||
.iter()
|
||||
.map(|field| {
|
||||
let ident = syn::Ident::new(&field.label, proc_macro2::Span::call_site());
|
||||
let ident = format_ident!("{}", &field.label);
|
||||
quote! {
|
||||
#ident
|
||||
}
|
||||
|
||||
+46
-43
@@ -1,5 +1,39 @@
|
||||
use serde_indexed::{DeserializeIndexed, SerializeIndexed};
|
||||
|
||||
/// buffer should be big enough to hold serialized object.
|
||||
fn cbor_serialize<T: serde::Serialize>(
|
||||
object: &T,
|
||||
buffer: &mut [u8],
|
||||
) -> Result<usize, serde_cbor::Error> {
|
||||
let writer = serde_cbor::ser::SliceWrite::new(buffer);
|
||||
let mut ser = serde_cbor::Serializer::new(writer);
|
||||
|
||||
object.serialize(&mut ser)?;
|
||||
|
||||
let writer = ser.into_inner();
|
||||
let size = writer.bytes_written();
|
||||
|
||||
Ok(size)
|
||||
}
|
||||
|
||||
/// may or may not modify buffer to hold temporary data.
|
||||
/// buffer may be longer than serialized T.
|
||||
fn cbor_deserialize<'de, T: serde::Deserialize<'de>>(
|
||||
buffer: &'de mut [u8],
|
||||
) -> Result<T, serde_cbor::Error> {
|
||||
let mut deserializer = serde_cbor::de::Deserializer::from_mut_slice(buffer);
|
||||
serde::Deserialize::deserialize(&mut deserializer)
|
||||
}
|
||||
|
||||
/// scratch should be big enough to hold temporary data.
|
||||
/// buffer must not have trailing data.
|
||||
fn cbor_deserialize_with_scratch<'de, T: serde::Deserialize<'de>>(
|
||||
buffer: &'de [u8],
|
||||
scratch: &'de mut [u8],
|
||||
) -> Result<T, serde_cbor::Error> {
|
||||
serde_cbor::de::from_slice_with_scratch(buffer, scratch)
|
||||
}
|
||||
|
||||
mod some_keys {
|
||||
use super::*;
|
||||
|
||||
@@ -7,7 +41,6 @@ mod some_keys {
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, SerializeIndexed, DeserializeIndexed)]
|
||||
#[serde_indexed(offset = 1)]
|
||||
// #[serde_indexed(skip_nones)]
|
||||
pub struct SomeKeys {
|
||||
pub number: i32,
|
||||
pub bytes: [u8; 7],
|
||||
@@ -34,55 +67,24 @@ mod some_keys {
|
||||
}
|
||||
|
||||
fn another_example() -> SomeKeys {
|
||||
let mut string = heapless::String::new();
|
||||
string.push_str("so serde").unwrap();
|
||||
|
||||
let mut vector = heapless::Vec::<u8, consts::U16>::new();
|
||||
vector.push(42).unwrap();
|
||||
|
||||
SomeKeys {
|
||||
number: -7,
|
||||
bytes: [37u8; 7],
|
||||
string,
|
||||
option: Some(0xff),
|
||||
vector,
|
||||
}
|
||||
let mut an_example = an_example();
|
||||
an_example.option = Some(0xff);
|
||||
an_example
|
||||
}
|
||||
|
||||
// in Python: cbor.dumps({1: -7, 2: [37]*7, 3: "so serde", 5: [42]*1})
|
||||
const SERIALIZED_AN_EXAMPLE: &'static [u8] =
|
||||
b"\xa4\x01&\x02\x87\x18%\x18%\x18%\x18%\x18%\x18%\x18%\x03hso serde\x05\x81\x18*";
|
||||
|
||||
// in Python: cbor.dumps({1: -7, 2: [37]*7, 3: "so serde", 4: 0xff, 5: [42]*1})
|
||||
const SERIALIZED_ANOTHER_EXAMPLE: &'static [u8] =
|
||||
b"\xa5\x01&\x02\x87\x18%\x18%\x18%\x18%\x18%\x18%\x18%\x03hso serde\x04\x18\xff\x05\x81\x18*";
|
||||
|
||||
fn cbor_serialize<T: serde::Serialize>(
|
||||
object: &T,
|
||||
buffer: &mut [u8],
|
||||
) -> Result<usize, serde_cbor::Error> {
|
||||
let writer = serde_cbor::ser::SliceWrite::new(buffer);
|
||||
let mut ser = serde_cbor::Serializer::new(writer);
|
||||
|
||||
object.serialize(&mut ser)?;
|
||||
|
||||
let writer = ser.into_inner();
|
||||
let size = writer.bytes_written();
|
||||
|
||||
Ok(size)
|
||||
}
|
||||
|
||||
fn cbor_deserialize<'de, T: serde::Deserialize<'de>>(
|
||||
buffer: &'de mut [u8],
|
||||
) -> Result<T, serde_cbor::Error> {
|
||||
let mut deserializer = serde_cbor::de::Deserializer::from_mut_slice(buffer);
|
||||
serde::de::Deserialize::deserialize(&mut deserializer)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize() {
|
||||
let example = an_example();
|
||||
|
||||
let mut buffer = [0u8; 1024];
|
||||
let mut buffer = [0u8; 64];
|
||||
let size = cbor_serialize(&example, &mut buffer).unwrap();
|
||||
|
||||
assert_eq!(&buffer[..size], SERIALIZED_AN_EXAMPLE);
|
||||
@@ -90,10 +92,9 @@ mod some_keys {
|
||||
|
||||
#[test]
|
||||
fn deserialize() {
|
||||
let mut buffer = [0u8; 1024];
|
||||
buffer[..SERIALIZED_AN_EXAMPLE.len()].copy_from_slice(SERIALIZED_AN_EXAMPLE);
|
||||
|
||||
let maybe_example: SomeKeys = cbor_deserialize(&mut buffer).unwrap();
|
||||
// no allocations need in this case.
|
||||
let maybe_example: SomeKeys =
|
||||
cbor_deserialize_with_scratch(SERIALIZED_AN_EXAMPLE, &mut []).unwrap();
|
||||
|
||||
assert_eq!(maybe_example, an_example());
|
||||
}
|
||||
@@ -102,7 +103,7 @@ mod some_keys {
|
||||
fn another_serialize() {
|
||||
let example = another_example();
|
||||
|
||||
let mut buffer = [0u8; 1024];
|
||||
let mut buffer = [0u8; 64];
|
||||
let size = cbor_serialize(&example, &mut buffer).unwrap();
|
||||
|
||||
assert_eq!(&buffer[..size], SERIALIZED_ANOTHER_EXAMPLE);
|
||||
@@ -110,7 +111,9 @@ mod some_keys {
|
||||
|
||||
#[test]
|
||||
fn another_deserialize() {
|
||||
let mut buffer = [0u8; 1024];
|
||||
// could also use `cbor_deserialize_with_scratch` in this case,
|
||||
// demonstrating the `cbor_deserialize` function.
|
||||
let mut buffer = [0u8; SERIALIZED_ANOTHER_EXAMPLE.len()];
|
||||
buffer[..SERIALIZED_ANOTHER_EXAMPLE.len()].copy_from_slice(SERIALIZED_ANOTHER_EXAMPLE);
|
||||
|
||||
let maybe_example: SomeKeys = cbor_deserialize(&mut buffer).unwrap();
|
||||
|
||||
Reference in New Issue
Block a user