Skip to content
Commits on Source (14)
......@@ -6,6 +6,14 @@
# minicbor
## `0.11.0`
- Depends on `minicbor-derive-0.7.0`.
## `0.10.1`
- Small bugfix release (see commit 68963dc for details).
## `0.10.0`
- ⚠️ **Breaking** ⚠️: By default `Decoder::skip` is not available anymore. It can be enabled with
......@@ -77,6 +85,12 @@
# minicbor-derive
## `0.7.0`
- Major internal refactoring to make attribute parsing more flexible and robust.
- Adds as new attributes `encode_bound`, `decode_bound` and `bound` to allow overriding the
generated type parameter bounds.
## `0.6.4`
- Improve hygiene (see merge request !7).
......@@ -116,6 +130,10 @@
# minicbor-io
## `0.6.0`
- Depends on `minicbor-0.11.0`.
## `0.5.0`
- Depends on `minicbor-0.10.0`.
......
[package]
name = "minicbor-derive"
version = "0.6.4"
version = "0.7.0"
authors = ["Toralf Wittner <tw@dtex.org>"]
license = "BlueOak-1.0.0"
edition = "2018"
......@@ -16,7 +16,7 @@ proc-macro = true
[dependencies]
proc-macro2 = "1.0.18"
quote = "1.0.7"
syn = { version = "1.0.33", features = ["extra-traits", "visit"] }
syn = { version = "1.0.72", features = ["derive", "extra-traits", "visit"] }
[dev-dependencies]
minicbor = { path = "../minicbor", features = ["std", "derive"] }
//! Attribute handling.
pub mod typeparam;
pub mod codec;
pub mod encoding;
pub mod idx;
use std::collections::HashMap;
use std::fmt;
use std::hash::Hash;
use std::iter;
use syn::spanned::Spanned;
pub use typeparam::TypeParams;
pub use codec::CustomCodec;
pub use encoding::Encoding;
pub use idx::Idx;
/// Recognised attributes.
#[derive(Debug, Clone)]
pub struct Attributes(Level, HashMap<Kind, Value>);
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
enum Kind {
Codec,
Encoding,
Index,
IndexOnly,
Transparent,
TypeParam
}
#[derive(Debug, Clone)]
enum Value {
Codec(CustomCodec, proc_macro2::Span),
Encoding(Encoding, proc_macro2::Span),
Index(Idx, proc_macro2::Span),
Span(proc_macro2::Span),
TypeParam(TypeParams, proc_macro2::Span)
}
#[derive(Debug, Copy, Clone)]
pub enum Level {
Enum,
Struct,
Variant,
Field
}
impl fmt::Display for Level {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Level::Enum => f.write_str("enum"),
Level::Struct => f.write_str("struct"),
Level::Variant => f.write_str("variant"),
Level::Field => f.write_str("field")
}
}
}
impl Attributes {
pub fn new(l: Level) -> Self {
Attributes(l, HashMap::new())
}
pub fn try_from_iter<'a, I>(l: Level, attrs: I) -> syn::Result<Self>
where
I: IntoIterator<Item = &'a syn::Attribute>
{
let mut this = Attributes::new(l);
for m in attrs.into_iter().map(|a| Attributes::try_from(l, a)) {
let m = m?;
for (k, v) in m.1.into_iter() {
this.try_insert(k, v)?;
}
}
Ok(this)
}
fn try_from(l: Level, a: &syn::Attribute) -> syn::Result<Attributes> {
let mut attrs = Attributes::new(l);
// #[n(...)]
if a.path.is_ident("n") {
let idx = parse_u32_arg(a).map(Idx::N)?;
attrs.try_insert(Kind::Index, Value::Index(idx, a.tokens.span()))?;
return Ok(attrs)
}
// #[b(...)]
if a.path.is_ident("b") {
let idx = parse_u32_arg(a).map(Idx::B)?;
attrs.try_insert(Kind::Index, Value::Index(idx, a.tokens.span()))?;
return Ok(attrs)
}
// #[cbor(...)]
let cbor =
if let syn::Meta::List(ml) = a.parse_meta()? {
if !ml.path.is_ident("cbor") {
return Ok(Attributes::new(l))
}
ml
} else {
return Ok(Attributes::new(l))
};
for nested in &cbor.nested {
match nested {
syn::NestedMeta::Meta(syn::Meta::Path(arg)) =>
if arg.is_ident("index_only") {
attrs.try_insert(Kind::IndexOnly, Value::Span(nested.span()))?
} else if arg.is_ident("transparent") {
attrs.try_insert(Kind::Transparent, Value::Span(nested.span()))?
} else if arg.is_ident("map") {
attrs.try_insert(Kind::Encoding, Value::Encoding(Encoding::Map, nested.span()))?
} else if arg.is_ident("array") {
attrs.try_insert(Kind::Encoding, Value::Encoding(Encoding::Array, nested.span()))?
} else {
return Err(syn::Error::new(nested.span(), "unknown attribute"))
}
syn::NestedMeta::Meta(syn::Meta::NameValue(arg)) =>
if arg.path.is_ident("encode_with") {
if let syn::Lit::Str(path) = &arg.lit {
let cc = CustomCodec::Encode(syn::parse_str(&path.value())?);
attrs.try_insert(Kind::Codec, Value::Codec(cc, nested.span()))?
} else {
return Err(syn::Error::new(arg.span(), "string required"))
}
} else if arg.path.is_ident("decode_with") {
if let syn::Lit::Str(path) = &arg.lit {
let cc = CustomCodec::Decode(syn::parse_str(&path.value())?);
attrs.try_insert(Kind::Codec, Value::Codec(cc, nested.span()))?
} else {
return Err(syn::Error::new(arg.span(), "string required"))
}
} else if arg.path.is_ident("with") {
if let syn::Lit::Str(path) = &arg.lit {
let cc = CustomCodec::Module(syn::parse_str(&path.value())?);
attrs.try_insert(Kind::Codec, Value::Codec(cc, nested.span()))?
} else {
return Err(syn::Error::new(arg.span(), "string required"))
}
} else if arg.path.is_ident("encode_bound") {
if let syn::Lit::Str(path) = &arg.lit {
let t: syn::TypeParam = syn::parse_str(&path.value())?;
let b = TypeParams::Encode(iter::once((t.ident.clone(), t)).collect());
attrs.try_insert(Kind::TypeParam, Value::TypeParam(b, nested.span()))?
} else {
return Err(syn::Error::new(arg.span(), "string required"))
}
} else if arg.path.is_ident("decode_bound") {
if let syn::Lit::Str(path) = &arg.lit {
let t: syn::TypeParam = syn::parse_str(&path.value())?;
let b = TypeParams::Decode(iter::once((t.ident.clone(), t)).collect());
attrs.try_insert(Kind::TypeParam, Value::TypeParam(b, nested.span()))?
} else {
return Err(syn::Error::new(arg.span(), "string required"))
}
} else if arg.path.is_ident("bound") {
if let syn::Lit::Str(path) = &arg.lit {
let t: syn::TypeParam = syn::parse_str(&path.value())?;
let m = iter::once((t.ident.clone(), t)).collect::<HashMap<_, _>>();
let b = TypeParams::Both { encode: m.clone(), decode: m };
attrs.try_insert(Kind::TypeParam, Value::TypeParam(b, nested.span()))?
} else {
return Err(syn::Error::new(arg.span(), "string required"))
}
} else {
return Err(syn::Error::new(nested.span(), "unknown attribute"))
}
syn::NestedMeta::Meta(syn::Meta::List(arg)) =>
if arg.path.is_ident("n") {
if let Some(syn::NestedMeta::Lit(syn::Lit::Int(n))) = arg.nested.first() {
let idx = parse_int(n).map(Idx::N)?;
attrs.try_insert(Kind::Index, Value::Index(idx, a.tokens.span()))?;
} else {
return Err(syn::Error::new(arg.span(), "n expects a u32 argument"))
}
} else if arg.path.is_ident("b") {
if let Some(syn::NestedMeta::Lit(syn::Lit::Int(n))) = arg.nested.first() {
let idx = parse_int(n).map(Idx::B)?;
attrs.try_insert(Kind::Index, Value::Index(idx, a.tokens.span()))?;
} else {
return Err(syn::Error::new(arg.span(), "b expects a u32 argument"))
}
} else {
return Err(syn::Error::new(nested.span(), "unknown attribute"))
}
syn::NestedMeta::Lit(_) => {
return Err(syn::Error::new(nested.span(), "unknown attribute"))
}
}
}
Ok(attrs)
}
pub fn encoding(&self) -> Option<Encoding> {
self.get(Kind::Encoding).and_then(|v| v.encoding())
}
pub fn index(&self) -> Option<Idx> {
self.get(Kind::Index).and_then(|v| v.index())
}
pub fn codec(&self) -> Option<&CustomCodec> {
self.get(Kind::Codec).and_then(|v| v.codec())
}
pub fn type_params(&self) -> Option<&TypeParams> {
self.get(Kind::TypeParam).and_then(|v| v.type_params())
}
pub fn transparent(&self) -> bool {
self.contains_key(Kind::Transparent)
}
pub fn index_only(&self) -> bool {
self.contains_key(Kind::IndexOnly)
}
fn contains_key(&self, k: Kind) -> bool {
self.1.contains_key(&k)
}
fn get(&self, k: Kind) -> Option<&Value> {
self.1.get(&k)
}
fn get_mut(&mut self, k: Kind) -> Option<&mut Value> {
self.1.get_mut(&k)
}
fn try_insert(&mut self, key: Kind, val: Value) -> syn::Result<()> {
match self.0 {
Level::Struct => match key {
Kind::Encoding | Kind::Transparent => {}
Kind::TypeParam | Kind::Codec | Kind::Index | Kind::IndexOnly => {
let msg = format!("attribute is not supported on {}-level", self.0);
return Err(syn::Error::new(val.span(), msg))
}
}
Level::Field => match key {
Kind::TypeParam | Kind::Codec | Kind::Index => {}
Kind::Encoding | Kind::IndexOnly | Kind::Transparent => {
let msg = format!("attribute is not supported on {}-level", self.0);
return Err(syn::Error::new(val.span(), msg))
}
}
Level::Enum => match key {
Kind::Encoding | Kind::IndexOnly => {}
Kind::TypeParam | Kind::Codec | Kind::Index | Kind::Transparent => {
let msg = format!("attribute is not supported on {}-level", self.0);
return Err(syn::Error::new(val.span(), msg))
}
}
Level::Variant => match key {
Kind::Encoding | Kind::Index => {}
Kind::TypeParam | Kind::Codec | Kind::IndexOnly | Kind::Transparent => {
let msg = format!("attribute is not supported on {}-level", self.0);
return Err(syn::Error::new(val.span(), msg))
}
}
}
if self.contains_key(key) {
if let Some(Value::Codec(cc, _)) = self.get_mut(key) {
let s = val.span();
match (val, &cc) {
(Value::Codec(CustomCodec::Encode(e), _), CustomCodec::Decode(d)) => {
*cc = CustomCodec::Both { encode: e, decode: d.clone() };
return Ok(())
}
(Value::Codec(CustomCodec::Decode(d), _), CustomCodec::Encode(e)) => {
*cc = CustomCodec::Both { encode: e.clone(), decode: d };
return Ok(())
}
_ => return Err(syn::Error::new(s, "duplicate attribute"))
}
} else if let Some(Value::TypeParam(cb, _)) = self.get_mut(key) {
let s = val.span();
if let Value::TypeParam(p, _) = val {
cb.try_merge(s, p)?;
return Ok(())
}
return Err(syn::Error::new(s, "duplicate attribute"))
} else {
return Err(syn::Error::new(val.span(), "duplicate attribute"))
}
}
self.1.insert(key, val);
Ok(())
}
}
impl Value {
fn span(&self) -> proc_macro2::Span {
match self {
Value::TypeParam(_, s) => *s,
Value::Codec(_, s) => *s,
Value::Encoding(_, s) => *s,
Value::Index(_, s) => *s,
Value::Span(s) => *s
}
}
fn index(&self) -> Option<Idx> {
if let Value::Index(i, _) = self {
Some(*i)
} else {
None
}
}
fn codec(&self) -> Option<&CustomCodec> {
if let Value::Codec(c, _) = self {
Some(c)
} else {
None
}
}
fn encoding(&self) -> Option<Encoding> {
if let Value::Encoding(e, _) = self {
Some(*e)
} else {
None
}
}
fn type_params(&self) -> Option<&TypeParams> {
if let Value::TypeParam(t, _) = self {
Some(t)
} else {
None
}
}
}
fn parse_u32_arg(a: &syn::Attribute) -> syn::Result<u32> {
parse_int(&a.parse_args()?)
}
fn parse_int(n: &syn::LitInt) -> syn::Result<u32> {
n.base10_digits()
.parse()
.map_err(|_| syn::Error::new(n.span(), "expected `u32` value"))
}
/// Custom encode/decode functions.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum CustomCodec {
/// Custom encode function.
///
/// Assumed to be of a type equivalent to:
///
/// `fn<T, W: Write>(&T, &mut Encoder<W>) -> Result<(), Error<W::Error>>`
///
/// Declared with `#[cbor(encode_with = "...")]`.
Encode(syn::ExprPath),
/// Custom decode function.
///
/// Assumed to be of a type equivalent to:
///
/// `fn<T>(&mut Decoder<'_>) -> Result<T, Error>`
///
/// Declared with `#[cbor(decode_with = "...")]`.
Decode(syn::ExprPath),
/// The combination of `encode_with` and `decode_with`.
Both {
encode: syn::ExprPath,
decode: syn::ExprPath
},
/// A module which contains custom encode/decode functions.
///
/// The module is assumed to contain two functions named `encode` and
/// `decode` whose types match those declared with
/// `#[cbor(encode_with = "...")]` or `#[cbor(decode_with = "...")]`
/// respectively. Declared with `#[cbor(with = "...")]`.
Module(syn::ExprPath)
}
impl CustomCodec {
/// Is this a custom codec from `encode_with` or `with`?
pub fn is_encode(&self) -> bool {
!matches!(self, CustomCodec::Decode(_))
}
/// Is this a custom codec from `decode_with` or `with`?
pub fn is_decode(&self) -> bool {
!matches!(self, CustomCodec::Encode(_))
}
/// Extract the encode function unless this `CustomCodec` does not declare one.
pub fn to_encode_path(&self) -> Option<syn::ExprPath> {
match self {
CustomCodec::Encode(p) => Some(p.clone()),
CustomCodec::Both { encode, .. } => Some(encode.clone()),
CustomCodec::Decode(_) => None,
CustomCodec::Module(p) => {
let mut p = p.clone();
let ident = syn::Ident::new("encode", proc_macro2::Span::call_site());
p.path.segments.push(ident.into());
Some(p)
}
}
}
/// Extract the decode function unless this `CustomCodec` does not declare one.
pub fn to_decode_path(&self) -> Option<syn::ExprPath> {
match self {
CustomCodec::Encode(_) => None,
CustomCodec::Decode(p) => Some(p.clone()),
CustomCodec::Both { decode, .. } => Some(decode.clone()),
CustomCodec::Module(p) => {
let mut p = p.clone();
let ident = syn::Ident::new("decode", proc_macro2::Span::call_site());
p.path.segments.push(ident.into());
Some(p)
}
}
}
}
/// The encoding to use for structs and enum variants.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Encoding {
Array,
Map
}
impl Default for Encoding {
fn default() -> Self {
Encoding::Array
}
}
use proc_macro2::Span;
use quote::{ToTokens, TokenStreamExt};
use std::collections::HashSet;
/// The index attribute.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Idx {
/// A regular, non-borrowing index.
N(u32),
/// An index which indicates that the value borrows from the decoding input.
B(u32)
}
impl ToTokens for Idx {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
tokens.append(proc_macro2::Literal::u32_unsuffixed(self.val()))
}
}
impl Idx {
/// Test if `Idx` is the `B` variant.
pub fn is_b(self) -> bool {
matches!(self, Idx::B(_))
}
/// Get the numeric index value.
pub fn val(self) -> u32 {
match self {
Idx::N(i) => i,
Idx::B(i) => i
}
}
}
/// Check that there are no duplicate `Idx` values in `iter`.
pub fn check_uniq<'a, I>(s: Span, iter: I) -> syn::Result<()>
where
I: IntoIterator<Item = &'a Idx>
{
let mut set = HashSet::new();
let mut ctr = 0;
for u in iter {
set.insert(u.val());
ctr += 1;
}
if ctr != set.len() {
return Err(syn::Error::new(s, "duplicate index numbers"))
}
Ok(())
}
use std::collections::HashMap;
use std::hash::Hash;
use std::mem;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TypeParams {
Encode(HashMap<syn::Ident, syn::TypeParam>),
Decode(HashMap<syn::Ident, syn::TypeParam>),
Both {
encode: HashMap<syn::Ident, syn::TypeParam>,
decode: HashMap<syn::Ident, syn::TypeParam>
}
}
impl TypeParams {
pub fn try_merge(&mut self, s: proc_macro2::Span, other: Self) -> syn::Result<()> {
match (&mut *self, other) {
(Self::Encode(e1), Self::Encode(e2)) => {
try_merge(s, e1, e2)?
}
(Self::Decode(d1), Self::Decode(d2)) => {
try_merge(s, d1, d2)?
}
(Self::Encode(e), Self::Decode(d)) => {
*self = Self::Both { encode: mem::take(e), decode: d }
}
(Self::Decode(d), Self::Encode(e)) => {
*self = Self::Both { encode: e, decode: mem::take(d) }
}
(Self::Encode(e1), Self::Both { encode: e2, decode }) => {
try_merge(s, e1, e2)?;
*self = Self::Both { encode: mem::take(e1), decode }
}
(Self::Decode(d1), Self::Both { encode, decode: d2 }) => {
try_merge(s, d1, d2)?;
*self = Self::Both { encode, decode: mem::take(d1) }
}
(Self::Both { encode: e1, .. }, Self::Encode(e2)) => {
try_merge(s, e1, e2)?
}
(Self::Both { decode: d1, .. }, Self::Decode(d2)) => {
try_merge(s, d1, d2)?
}
(Self::Both { encode: e1, decode: d1 }, Self::Both { encode: e2, decode: d2 }) => {
try_merge(s, e1, e2)?;
try_merge(s, d1, d2)?
}
}
Ok(())
}
pub fn get_encode(&self, id: &syn::Ident) -> Option<&syn::TypeParam> {
match self {
Self::Decode(_) => None,
Self::Encode(e) => e.get(id),
Self::Both { encode, .. } => encode.get(id),
}
}
pub fn get_decode(&self, id: &syn::Ident) -> Option<&syn::TypeParam> {
match self {
Self::Encode(_) => None,
Self::Decode(d) => d.get(id),
Self::Both { decode, .. } => decode.get(id)
}
}
}
fn try_merge<K, V>(s: proc_macro2::Span, a: &mut HashMap<K, V>, b: HashMap<K, V>) -> syn::Result<()>
where
K: Eq + Hash
{
for (k, v) in b.into_iter() {
if a.contains_key(&k) {
return Err(syn::Error::new(s, "duplicate type parameter"))
}
a.insert(k, v);
}
Ok(())
}
#![allow(clippy::type_complexity)]
use crate::{check_uniq, field_indices, index_number, is_cow, is_option, variant_indices};
use crate::{Idx, lifetimes_to_constrain, is_str, is_byte_slice, encoding, Encoding};
use crate::{collect_type_params, CustomCodec, custom_codec};
use crate::find_cbor_attr;
use crate::Mode;
use crate::{add_bound_to_type_params, collect_type_params, is_cow, is_option, is_str, is_byte_slice};
use crate::attrs::{Attributes, CustomCodec, Encoding, Level};
use crate::fields::Fields;
use crate::variants::Variants;
use crate::lifetimes::{gen_lifetime, lifetimes_to_constrain, add_lifetime};
use quote::quote;
use std::collections::HashSet;
use syn::spanned::Spanned;
......@@ -31,45 +31,60 @@ fn on_struct(inp: &mut syn::DeriveInput) -> syn::Result<proc_macro2::TokenStream
unreachable!("`derive_from` matched against `syn::Data::Struct`")
};
let name = &inp.ident;
let indices = field_indices(data.fields.iter())?;
check_uniq(name.span(), indices.iter().cloned())?;
let name = &inp.ident;
let attrs = Attributes::try_from_iter(Level::Struct, inp.attrs.iter())?;
let fields = Fields::try_from(name.span(), data.fields.iter())?;
let decode_fns: Vec<Option<CustomCodec>> = fields.attrs.iter()
.map(|a| a.codec().cloned().filter(CustomCodec::is_decode))
.collect();
if find_cbor_attr(inp.attrs.iter(), "index_only", false)?.is_some() {
return Err(syn::Error::new(inp.span(), "index_only is not supported on structs"))
let mut lifetime = gen_lifetime()?;
for l in lifetimes_to_constrain(fields.indices.iter().zip(fields.types.iter())) {
if !lifetime.bounds.iter().any(|b| *b == l) {
lifetime.bounds.push(l.clone())
}
}
let (field_names, field_types, decode_fns) = fields(data.fields.iter())?;
let mut lifetime = decode_lifetime()?;
for l in lifetimes_to_constrain(indices.iter().zip(field_types.iter())) {
lifetime.bounds.push(l.clone())
// Collect type parameters which should not have a `Decode` bound added,
// i.e. from fields which have a custom decode function defined.
let blacklist = {
let iter = data.fields.iter()
.zip(&decode_fns)
.filter_map(|(f, ff)| ff.is_some().then(|| f));
collect_type_params(&inp.generics, iter)
};
{
let bound = gen_decode_bound()?;
let params = inp.generics.type_params_mut();
add_bound_to_type_params(bound, params, &blacklist, &fields.attrs, Mode::Decode);
}
// Collect type parameters which should not have an `Decode` bound added.
let blacklist = collect_type_params(&inp.generics, data.fields.iter().zip(&decode_fns).filter_map(|(f, ff)| {
if let Some(CustomCodec::Decode(_)) | Some(CustomCodec::Both(_)) = ff {
Some(f)
} else {
None
}
}));
add_decode_bound(&blacklist, inp.generics.type_params_mut())?;
let g = add_lifetime(&inp.generics, lifetime);
let (impl_generics , ..) = g.split_for_impl();
let (impl_generics, ..) = g.split_for_impl();
let (_, typ_generics, where_clause) = inp.generics.split_for_impl();
// If transparent, just forward the decode call to the inner type.
if find_cbor_attr(inp.attrs.iter(), "transparent", false)?.is_some() {
return make_transparent_impl(inp, data, impl_generics, typ_generics, where_clause)
if attrs.transparent() {
if fields.len() != 1 {
let msg = "#[cbor(transparent)] requires a struct with one field";
return Err(syn::Error::new(inp.ident.span(), msg))
}
let f = data.fields.iter().next().expect("struct has 1 field");
let a = fields.attrs.first().expect("struct has 1 field");
return make_transparent_impl(&inp.ident, f, a, impl_generics, typ_generics, where_clause)
}
let field_str = field_names.iter().map(|n| format!("{}::{}", name, n));
let encoding = inp.attrs.iter().find_map(encoding).unwrap_or_default();
let statements = gen_statements(&field_names, &field_types, &indices, &decode_fns, encoding)?;
let field_str = fields.idents.iter().map(|n| format!("{}::{}", name, n)).collect::<Vec<_>>();
let statements = gen_statements(&fields, &decode_fns, attrs.encoding().unwrap_or_default())?;
let Fields { indices, idents, .. } = fields;
let result = if let syn::Fields::Named(_) = data.fields {
quote! {
Ok(#name {
#(#field_names : if let Some(x) = #field_names {
#(#idents : if let Some(x) = #idents {
x
} else {
return Err(minicbor::decode::Error::MissingValue(#indices, #field_str))
......@@ -80,7 +95,7 @@ fn on_struct(inp: &mut syn::DeriveInput) -> syn::Result<proc_macro2::TokenStream
quote!(Ok(#name))
} else {
quote! {
Ok(#name(#(if let Some(x) = #field_names {
Ok(#name(#(if let Some(x) = #idents {
x
} else {
return Err(minicbor::decode::Error::MissingValue(#indices, #field_str))
......@@ -89,8 +104,8 @@ fn on_struct(inp: &mut syn::DeriveInput) -> syn::Result<proc_macro2::TokenStream
};
Ok(quote! {
impl #impl_generics minicbor::Decode<'__b777> for #name #typ_generics #where_clause {
fn decode(__d777: &mut minicbor::Decoder<'__b777>) -> core::result::Result<#name #typ_generics, minicbor::decode::Error> {
impl #impl_generics minicbor::Decode<'bytes> for #name #typ_generics #where_clause {
fn decode(__d777: &mut minicbor::Decoder<'bytes>) -> core::result::Result<#name #typ_generics, minicbor::decode::Error> {
#statements
#result
}
......@@ -107,20 +122,20 @@ fn on_enum(inp: &mut syn::DeriveInput) -> syn::Result<proc_macro2::TokenStream>
unreachable!("`derive_from` matched against `syn::Data::Enum`")
};
let name = &inp.ident;
check_uniq(data.enum_token.span(), variant_indices(data.variants.iter())?)?;
let index_only = find_cbor_attr(inp.attrs.iter(), "index_only", false)?.is_some();
let name = &inp.ident;
let enum_attrs = Attributes::try_from_iter(Level::Enum, inp.attrs.iter())?;
let enum_encoding = enum_attrs.encoding().unwrap_or_default();
let index_only = enum_attrs.index_only();
let variants = Variants::try_from(name.span(), data.variants.iter())?;
let enum_encoding = inp.attrs.iter().find_map(encoding).unwrap_or_default();
let mut blacklist = HashSet::new();
let mut lifetime = decode_lifetime()?;
let mut field_attrs = Vec::new();
let mut lifetime = gen_lifetime()?;
let mut rows = Vec::new();
for var in data.variants.iter() {
for ((var, idx), attrs) in data.variants.iter().zip(variants.indices.iter()).zip(&variants.attrs) {
let fields = Fields::try_from(var.ident.span(), var.fields.iter())?;
let encoding = attrs.encoding().unwrap_or(enum_encoding);
let con = &var.ident;
let idx = index_number(var.ident.span(), &var.attrs)?;
let indices = field_indices(var.fields.iter())?;
check_uniq(con.span(), indices.iter().cloned())?;
let row = if let syn::Fields::Unit = &var.fields {
if index_only {
quote!(#idx => Ok(#name::#con),)
......@@ -131,31 +146,36 @@ fn on_enum(inp: &mut syn::DeriveInput) -> syn::Result<proc_macro2::TokenStream>
})
}
} else {
let (field_names, field_types, decode_fns) = fields(var.fields.iter())?;
for l in lifetimes_to_constrain(indices.iter().zip(field_types.iter())) {
lifetime.bounds.push(l.clone())
}
let field_str = field_names.iter().map(|n| format!("{}::{}::{}", name, con, n));
let numbers = field_indices(var.fields.iter())?;
let encoding = var.attrs.iter().find_map(encoding).unwrap_or(enum_encoding);
// Collect type parameters which should not have an `Decode` bound added.
blacklist.extend(collect_type_params(&inp.generics, var.fields.iter().zip(&decode_fns).filter_map(|(f, ff)| {
if let Some(CustomCodec::Decode(_)) | Some(CustomCodec::Both(_)) = ff {
Some(f)
} else {
None
for l in lifetimes_to_constrain(fields.indices.iter().zip(fields.types.iter())) {
if !lifetime.bounds.iter().any(|b| *b == l) {
lifetime.bounds.push(l.clone())
}
})));
let statements = gen_statements(&field_names, &field_types, &numbers, &decode_fns, encoding)?;
}
let decode_fns: Vec<Option<CustomCodec>> = fields.attrs.iter()
.map(|a| a.codec().cloned().filter(CustomCodec::is_decode))
.collect();
let field_str = fields.idents.iter()
.map(|n| format!("{}::{}::{}", name, con, n))
.collect::<Vec<_>>();
// Collect type parameters which should not have an `Decode` bound added,
// i.e. from fields which have a custom decode function defined.
blacklist.extend({
let iter = var.fields.iter()
.zip(&decode_fns)
.filter_map(|(f, ff)| ff.is_some().then(|| f));
collect_type_params(&inp.generics, iter)
});
let statements = gen_statements(&fields, &decode_fns, encoding)?;
let Fields { indices, idents, .. } = fields;
if let syn::Fields::Named(_) = var.fields {
quote! {
#idx => {
#statements
Ok(#name::#con {
#(#field_names : if let Some(x) = #field_names {
#(#idents : if let Some(x) = #idents {
x
} else {
return Err(minicbor::decode::Error::MissingValue(#numbers, #field_str))
return Err(minicbor::decode::Error::MissingValue(#indices, #field_str))
}),*
})
}
......@@ -164,24 +184,29 @@ fn on_enum(inp: &mut syn::DeriveInput) -> syn::Result<proc_macro2::TokenStream>
quote! {
#idx => {
#statements
Ok(#name::#con(#(if let Some(x) = #field_names {
Ok(#name::#con(#(if let Some(x) = #idents {
x
} else {
return Err(minicbor::decode::Error::MissingValue(#numbers, #field_str))
return Err(minicbor::decode::Error::MissingValue(#indices, #field_str))
}),*))
}
}
}
};
field_attrs.extend_from_slice(&fields.attrs);
rows.push(row)
}
add_decode_bound(&blacklist, inp.generics.type_params_mut())?;
{
let bound = gen_decode_bound()?;
let params = inp.generics.type_params_mut();
add_bound_to_type_params(bound, params, &blacklist, &field_attrs, Mode::Decode);
}
let g = add_lifetime(&inp.generics, lifetime);
let (impl_generics , ..) = g.split_for_impl();
let (_, typ_generics, where_clause) = inp.generics.split_for_impl();
let check = if index_only {
quote!()
} else {
......@@ -193,8 +218,8 @@ fn on_enum(inp: &mut syn::DeriveInput) -> syn::Result<proc_macro2::TokenStream>
};
Ok(quote! {
impl #impl_generics minicbor::Decode<'__b777> for #name #typ_generics #where_clause {
fn decode(__d777: &mut minicbor::Decoder<'__b777>) -> core::result::Result<#name #typ_generics, minicbor::decode::Error> {
impl #impl_generics minicbor::Decode<'bytes> for #name #typ_generics #where_clause {
fn decode(__d777: &mut minicbor::Decoder<'bytes>) -> core::result::Result<#name #typ_generics, minicbor::decode::Error> {
#check
match __d777.u32()? {
#(#rows)*
......@@ -223,21 +248,12 @@ fn on_enum(inp: &mut syn::DeriveInput) -> syn::Result<proc_macro2::TokenStream>
// [1]: These variables will later be deconstructed in `on_enum` and
// `on_struct` and their inner value will be used to initialise a field.
// If not present, an error will be produced.
fn gen_statements
( names: &[syn::Ident]
, types: &[syn::Type]
, numbers: &[Idx]
, decode_fns: &[Option<CustomCodec>]
, encoding: Encoding
) -> syn::Result<proc_macro2::TokenStream>
{
assert_eq!(names.len(), types.len());
assert_eq!(types.len(), numbers.len());
assert_eq!(numbers.len(), decode_fns.len());
fn gen_statements(fields: &Fields, decode_fns: &[Option<CustomCodec>], encoding: Encoding) -> syn::Result<proc_macro2::TokenStream> {
assert_eq!(fields.len(), decode_fns.len());
let default_decode_fn: syn::ExprPath = syn::parse_str("minicbor::Decode::decode")?;
let inits = types.iter().map(|ty| {
let inits = fields.types.iter().map(|ty| {
if is_option(ty, |_| true) {
quote!(Some(None))
} else {
......@@ -245,7 +261,7 @@ fn gen_statements
}
});
let actions = numbers.iter().zip(names.iter().zip(types.iter().zip(decode_fns)))
let actions = fields.indices.iter().zip(fields.idents.iter().zip(fields.types.iter().zip(decode_fns)))
.map(|(ix, (name, (ty, ff)))| {
let decode_fn = ff.as_ref()
.and_then(|ff| ff.to_decode_path())
......@@ -272,14 +288,16 @@ fn gen_statements
})
.collect::<Vec<_>>();
let Fields { idents, types, indices, .. } = fields;
Ok(match encoding {
Encoding::Array => quote! {
#(let mut #names : core::option::Option<#types> = #inits;)*
#(let mut #idents : core::option::Option<#types> = #inits;)*
if let Some(__len777) = __d777.array()? {
for __i777 in 0 .. __len777 {
match __i777 {
#(#numbers => #actions)*
#(#indices => #actions)*
_ => __d777.skip()?
}
}
......@@ -287,7 +305,7 @@ fn gen_statements
let mut __i777 = 0;
while minicbor::data::Type::Break != __d777.datatype()? {
match __i777 {
#(#numbers => #actions)*
#(#indices => #actions)*
_ => __d777.skip()?
}
__i777 += 1
......@@ -296,19 +314,19 @@ fn gen_statements
}
},
Encoding::Map => quote! {
#(let mut #names : core::option::Option<#types> = #inits;)*
#(let mut #idents : core::option::Option<#types> = #inits;)*
if let Some(__len777) = __d777.map()? {
for _ in 0 .. __len777 {
match __d777.u32()? {
#(#numbers => #actions)*
#(#indices => #actions)*
_ => __d777.skip()?
}
}
} else {
while minicbor::data::Type::Break != __d777.datatype()? {
match __d777.u32()? {
#(#numbers => #actions)*
#(#indices => #actions)*
_ => __d777.skip()?
}
}
......@@ -318,97 +336,22 @@ fn gen_statements
})
}
/// Map fields to a list of field names and field types.
fn fields<'a, I>(iter: I) -> syn::Result<(Vec<syn::Ident>, Vec<syn::Type>, Vec<Option<CustomCodec>>)>
where
I: Iterator<Item = &'a syn::Field> + Clone
{
let names = iter.clone()
.enumerate()
.map(|(i, f)| {
match &f.ident {
Some(n) => n.clone(),
None => quote::format_ident!("_{}", i)
}
})
.collect();
let types = iter.clone()
.map(|f| f.ty.clone())
.collect();
let decode_fns = iter
.map(|f| {
for a in &f.attrs {
if let Some(ff) = custom_codec(a)? {
if ff.is_decode() {
return Ok(Some(ff))
}
}
}
Ok(None)
})
.collect::<syn::Result<Vec<_>>>()?;
Ok((names, types, decode_fns))
}
/// Generate the '__b777 lifetime.
fn decode_lifetime() -> syn::Result<syn::LifetimeDef> {
syn::parse_str("'__b777")
}
/// Add a `minicbor::Decode` bound to every type parameter.
fn add_decode_bound<'a, I>(blacklist: &HashSet<syn::TypeParam>, iter: I) -> syn::Result<()>
where
I: Iterator<Item = &'a mut syn::TypeParam>
{
let bound: syn::TypeParamBound = syn::parse_str("minicbor::Decode<'__b777>")?;
for t in iter.filter(|t| !blacklist.contains(t)) {
t.bounds.push(bound.clone())
}
Ok(())
}
/// Return a modified clone of `syn::Generics` with the given lifetime
/// parameter put before the other type and lifetime parameters.
///
/// This will be used later when splitting the parameters so that the
/// additional lifetime is only present in the `impl` parameter section.
fn add_lifetime(g: &syn::Generics, l: syn::LifetimeDef) -> syn::Generics {
let mut g2 = g.clone();
g2.params = Some(l.into()).into_iter().chain(g2.params).collect();
g2
}
/// Forward the decoding because of a `#[cbor(transparent)]` attribute.
fn make_transparent_impl
( input: &syn::DeriveInput
, data: &syn::DataStruct
( name: &syn::Ident
, field: &syn::Field
, attrs: &Attributes
, impl_generics: syn::ImplGenerics
, typ_generics: syn::TypeGenerics
, where_clause: Option<&syn::WhereClause>
) -> syn::Result<proc_macro2::TokenStream>
{
if data.fields.len() != 1 {
let msg = "#[cbor(transparent)] requires a struct with one field";
return Err(syn::Error::new(input.ident.span(), msg))
}
let field = data.fields.iter().next().expect("struct has one field");
if let Some(a) = find_cbor_attr(field.attrs.iter(), "decode_with", true)? {
let msg = "#[cbor(decode_with)] not allowed within #[cbor(transparent)]";
return Err(syn::Error::new(a.span(), msg))
}
if let Some(a) = find_cbor_attr(field.attrs.iter(), "with", true)? {
let msg = "#[cbor(with)] not allowed within #[cbor(transparent)]";
return Err(syn::Error::new(a.span(), msg))
if attrs.codec().map(CustomCodec::is_decode).unwrap_or(false) {
let msg = "`decode_with` or `with` not allowed with #[cbor(transparent)]";
let span = field.ident.as_ref().map(|i| i.span()).unwrap_or_else(|| field.span());
return Err(syn::Error::new(span, msg))
}
let name = &input.ident;
let call =
if let Some(id) = &field.ident {
quote! {
......@@ -421,11 +364,14 @@ fn make_transparent_impl
};
Ok(quote! {
impl #impl_generics minicbor::Decode<'__b777> for #name #typ_generics #where_clause {
fn decode(__d777: &mut minicbor::Decoder<'__b777>) -> core::result::Result<#name #typ_generics, minicbor::decode::Error> {
impl #impl_generics minicbor::Decode<'bytes> for #name #typ_generics #where_clause {
fn decode(__d777: &mut minicbor::Decoder<'bytes>) -> core::result::Result<#name #typ_generics, minicbor::decode::Error> {
#call
}
}
})
}
fn gen_decode_bound() -> syn::Result<syn::TypeParamBound> {
syn::parse_str("minicbor::Decode<'bytes>")
}
This diff is collapsed.
use crate::attrs::{Attributes, Idx, Level};
use crate::attrs::idx;
use proc_macro2::Span;
use syn::{Ident, Type};
#[derive(Debug, Clone)]
pub struct Fields {
/// field position
pub pos: Vec<usize>,
/// field identifiers
pub idents: Vec<Ident>,
/// does the field hava a name or is the identifier generated
pub is_name: Vec<bool>,
/// CBOR indices of fields
pub indices: Vec<Idx>,
/// field types
pub types: Vec<Type>,
/// field attributes
pub attrs: Vec<Attributes>
}
impl Fields {
pub fn try_from<'a, I>(span: Span, fields: I) -> syn::Result<Self>
where
I: IntoIterator<Item = &'a syn::Field>
{
let mut pos = Vec::new();
let mut indices = Vec::new();
let mut idents = Vec::new();
let mut is_name = Vec::new();
let mut types = Vec::new();
let mut attrs = Vec::new();
let sorted = {
let mut v = Vec::new();
for (i, f) in fields.into_iter().enumerate() {
let attr = Attributes::try_from_iter(Level::Field, &f.attrs)?;
let idex = attr.index().ok_or_else(|| {
let s = f.ident.as_ref().map(|i| i.span()).unwrap_or(span);
syn::Error::new(s, "missing `#[n(...)]` or `#[b(...)]` attribute")
})?;
let (idnt, is_name) = match &f.ident {
Some(n) => (n.clone(), true),
None => (quote::format_ident!("_{}", i), false)
};
let typ = f.ty.clone();
v.push((i, idex, idnt, is_name, typ, attr))
}
v.sort_unstable_by_key(|(_, n, ..)| n.val());
v
};
for (i, idx, ident, is, typ, attr) in sorted.into_iter() {
pos.push(i);
indices.push(idx);
idents.push(ident);
is_name.push(is);
types.push(typ);
attrs.push(attr);
}
idx::check_uniq(span, &indices)?;
Ok(Fields { pos, idents, is_name, indices, types, attrs })
}
pub fn len(&self) -> usize {
self.pos.len()
}
}
This diff is collapsed.
use crate::{is_str, is_byte_slice};
use crate::attrs::Idx;
use std::collections::HashSet;
/// Generate the decode lifetime.
pub fn gen_lifetime() -> syn::Result<syn::LifetimeDef> {
syn::parse_str("'bytes")
}
/// Return a modified clone of `syn::Generics` with the given lifetime
/// parameter put before the other type and lifetime parameters.
pub fn add_lifetime(g: &syn::Generics, l: syn::LifetimeDef) -> syn::Generics {
let mut g2 = g.clone();
g2.params = Some(l.into()).into_iter().chain(g2.params).collect();
g2
}
/// Get the set of lifetimes which need to be constrained to the decoding input lifetime.
pub fn lifetimes_to_constrain<'a, I>(types: I) -> HashSet<syn::Lifetime>
where
I: Iterator<Item = (&'a Idx, &'a syn::Type)>
{
// Get the lifetime of a reference if its type matches the predicate.
fn tyref_lifetime(ty: &syn::Type, pred: impl FnOnce(&syn::Type) -> bool) -> Option<syn::Lifetime> {
if let syn::Type::Reference(p) = ty {
if pred(&*p.elem) {
return p.lifetime.clone()
}
}
None
}
// Get all lifetimes of a type.
fn get_lifetimes(ty: &syn::Type, set: &mut HashSet<syn::Lifetime>) {
match ty {
syn::Type::Array(t) => get_lifetimes(&t.elem, set),
syn::Type::Slice(t) => get_lifetimes(&t.elem, set),
syn::Type::Paren(t) => get_lifetimes(&t.elem, set),
syn::Type::Group(t) => get_lifetimes(&t.elem, set),
syn::Type::Ptr(t) => get_lifetimes(&t.elem, set),
syn::Type::Reference(t) => {
if let Some(l) = &t.lifetime {
set.insert(l.clone());
}
get_lifetimes(&t.elem, set)
}
syn::Type::Tuple(t) => {
for t in &t.elems {
get_lifetimes(t, set)
}
}
syn::Type::Path(t) => {
for s in &t.path.segments {
if let syn::PathArguments::AngleBracketed(b) = &s.arguments {
for a in &b.args {
match a {
syn::GenericArgument::Type(t) => get_lifetimes(t, set),
syn::GenericArgument::Binding(b) => get_lifetimes(&b.ty, set),
syn::GenericArgument::Lifetime(l) => { set.insert(l.clone()); }
_ => {}
}
}
}
}
}
_ => {}
}
}
// Get the lifetime of the given type if it is an `Option` whose inner type matches the predicate.
fn option_lifetime(ty: &syn::Type, pred: impl FnOnce(&syn::Type) -> bool) -> Option<syn::Lifetime> {
if let syn::Type::Path(t) = ty {
if let Some(s) = t.path.segments.last() {
if s.ident == "Option" {
if let syn::PathArguments::AngleBracketed(b) = &s.arguments {
if b.args.len() == 1 {
if let syn::GenericArgument::Type(syn::Type::Reference(ty)) = &b.args[0] {
if pred(&*ty.elem) {
return ty.lifetime.clone()
}
}
}
}
}
}
}
None
}
let mut set = HashSet::new();
for (i, t) in types {
if let Some(l) = tyref_lifetime(t, is_str) {
set.insert(l);
continue
}
if let Some(l) = tyref_lifetime(t, is_byte_slice) {
set.insert(l);
continue
}
if let Some(l) = option_lifetime(t, is_str) {
set.insert(l);
continue
}
if let Some(l) = option_lifetime(t, is_byte_slice) {
set.insert(l);
continue
}
if i.is_b() {
get_lifetimes(t, &mut set)
}
}
set
}
use crate::attrs::{Attributes, Idx, Level};
use crate::attrs::idx;
use proc_macro2::Span;
#[derive(Debug, Clone)]
pub struct Variants {
/// CBOR indices of variants
pub indices: Vec<Idx>,
/// variant attributes
pub attrs: Vec<Attributes>
}
impl Variants {
pub fn try_from<'a, I>(span: Span, iter: I) -> syn::Result<Self>
where
I: IntoIterator<Item = &'a syn::Variant>
{
let mut indices = Vec::new();
let mut attrs = Vec::new();
for v in iter.into_iter() {
let attr = Attributes::try_from_iter(Level::Variant, &v.attrs)?;
let idex = attr.index().ok_or_else(|| {
syn::Error::new(v.ident.span(), "missing `#[n(...)]` or `#[b(...)]` attribute")
})?;
indices.push(idex);
attrs.push(attr);
}
idx::check_uniq(span, &indices)?;
Ok(Variants { indices, attrs })
}
}
[package]
name = "minicbor-io"
version = "0.5.0"
version = "0.6.0"
authors = ["Toralf Wittner <tw@dtex.org>"]
license = "BlueOak-1.0.0"
edition = "2018"
......@@ -20,10 +20,10 @@ async-io = ["futures-core", "futures-io", "futures-util"]
futures-core = { version = "0.3.8", optional = true }
futures-io = { version = "0.3.8", optional = true }
futures-util = { version = "0.3.8", features = ["io"], optional = true }
minicbor = { path = "../minicbor", version = "0.10.0", features = ["std"] }
minicbor = { path = "../minicbor", version = "0.11.0", features = ["std"] }
[dev-dependencies]
minicbor = { path = "../minicbor", version = "0.10.0", features = ["std", "derive"] }
minicbor = { path = "../minicbor", version = "0.11.0", features = ["std", "derive"] }
minicbor-io = { path = ".", features = ["async-io"] }
quickcheck = "1.0.1"
rand = "0.8"
......
[package]
name = "minicbor"
version = "0.10.0"
version = "0.11.0"
authors = ["Toralf Wittner <tw@dtex.org>"]
license = "BlueOak-1.0.0"
edition = "2018"
......@@ -23,7 +23,7 @@ partial-derive-support = ["minicbor-derive", "partial-skip-support"]
__test-partial-skip-support = []
[dependencies]
minicbor-derive = { version = "0.6.4", path = "../minicbor-derive", optional = true }
minicbor-derive = { version = "0.7.0", path = "../minicbor-derive", optional = true }
half = { version = "1", default-features = false, optional = true }
[dev-dependencies]
......
......@@ -394,7 +394,7 @@ impl<'b> Decoder<'b> {
let mut nrounds = 1u64; // number of iterations over array and map elements
let mut irounds = 0u64; // number of indefinite iterations
let mut stack: Vec<Option<u64>> = alloc::vec::Vec::new();
let mut stack: alloc::vec::Vec<Option<u64>> = alloc::vec::Vec::new();
while nrounds > 0 || irounds > 0 || !stack.is_empty() {
match self.current()? {
......