Skip to content

Commit

Permalink
Upgrade to syn 2.0.
Browse files Browse the repository at this point in the history
  • Loading branch information
wojciech-graj committed Nov 10, 2024
1 parent 6a392c9 commit c11be67
Show file tree
Hide file tree
Showing 12 changed files with 104 additions and 202 deletions.
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# v0.5.1
- Make `write_value` field in `#[protocol(tag(...))]` only required when deriving `ProtocolWrite`
# v0.6.0
- Allow multiple attributes in a single `#[protocol(...)]`
- Require unquoted expressions in attributes
- Impose `non_exhaustive` on `Error`
# v0.5.0
- Split `Protocol` into `ProtocolRead` and `ProtocolWrite`
Expand Down
8 changes: 4 additions & 4 deletions bin-proto-derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bin-proto-derive"
version = "0.5.0"
version = "0.6.0"
authors = [
"Wojciech Graj <wojtek.graj.2004@gmail.com>",
"Dylan McKay <me@dylanmckay.io>"
Expand All @@ -18,6 +18,6 @@ keywords = ["protocol", "binary", "bit", "codec", "serde"]
proc-macro = true

[dependencies]
syn = { version = "1.0.109", features = ["full"] }
quote = "1.0.36"
proc-macro2 = "1.0.82"
syn = { version = "2.0.87", features = ["full"] }
quote = "1.0.37"
proc-macro2 = "1.0.89"
178 changes: 48 additions & 130 deletions bin-proto-derive/src/attr.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
use proc_macro2::{Span, TokenStream};
use syn::{parse::Parser, punctuated::Punctuated, spanned::Spanned, token::Add, Error, Result};
use syn::{punctuated::Punctuated, spanned::Spanned, token::Plus, Error, Result};

#[derive(Default)]
pub struct Attrs {
pub discriminant_type: Option<syn::Type>,
pub discriminant: Option<syn::Expr>,
pub ctx: Option<syn::Type>,
pub ctx_bounds: Option<Punctuated<syn::TypeParamBound, Add>>,
pub ctx_bounds: Option<Punctuated<syn::TypeParamBound, Plus>>,
pub write_value: Option<syn::Expr>,
pub bits: Option<u32>,
pub bits: Option<syn::Expr>,
pub flexible_array_member: bool,
pub tag: Option<Tag>,
}
Expand Down Expand Up @@ -148,138 +148,56 @@ impl Attrs {
impl TryFrom<&[syn::Attribute]> for Attrs {
type Error = syn::Error;

#[allow(clippy::too_many_lines)]
fn try_from(value: &[syn::Attribute]) -> Result<Self> {
let meta_lists = value.iter().filter_map(|attr| match attr.parse_meta() {
Ok(syn::Meta::List(meta_list)) => {
if meta_list.path.get_ident()
== Some(&syn::Ident::new("protocol", Span::call_site()))
{
Some(meta_list)
} else {
None
}
}
_ => None,
});

fn try_from(attrs: &[syn::Attribute]) -> Result<Self> {
let mut attribs = Attrs::default();
for meta_list in meta_lists {
for meta in &meta_list.nested {
match meta {
syn::NestedMeta::Meta(syn::Meta::NameValue(name_value)) => match name_value
.path
.get_ident()
{
Some(ident) => match ident.to_string().as_str() {
"discriminant_type" => {
attribs.discriminant_type =
Some(meta_name_value_to_parse(name_value)?);
}
"discriminant" => {
attribs.discriminant = Some(meta_name_value_to_parse(name_value)?);
}
"ctx" => attribs.ctx = Some(meta_name_value_to_parse(name_value)?),
"ctx_bounds" => {
attribs.ctx_bounds =
Some(meta_name_value_to_punctuated(name_value)?);
}
"bits" => attribs.bits = Some(meta_name_value_to_u32(name_value)?),
"write_value" => {
attribs.write_value = Some(meta_name_value_to_parse(name_value)?);
}
"tag" => {
attribs.tag =
Some(Tag::External(meta_name_value_to_parse(name_value)?));
}
_ => return Err(Error::new(ident.span(), "unrecognised attribute")),
},
None => return Err(Error::new(meta.span(), "failed to parse attribute")),
},
syn::NestedMeta::Meta(syn::Meta::Path(path)) => match path.get_ident() {
Some(ident) => match ident.to_string().as_str() {
"flexible_array_member" => attribs.flexible_array_member = true,
_ => return Err(Error::new(ident.span(), "unrecognised attribute")),
},
None => {
return Err(Error::new(
path.get_ident().span(),
"expected identifier 1234",
));
}
},
syn::NestedMeta::Meta(syn::Meta::List(list)) => {
let mut typ = None;
let mut write_value = None;
for nested in &list.nested {
let name_value =
if let syn::NestedMeta::Meta(syn::Meta::NameValue(name_value)) =
nested
{
name_value
} else {
return Err(Error::new(list.span(), "unrecognized attribute"));
};
let ident = if let Some(ident) = name_value.path.get_ident() {
ident
} else {
return Err(Error::new(
name_value.span(),
"unrecognized attribute",
));
};
match ident.to_string().as_str() {
"type" => typ = Some(meta_name_value_to_parse(name_value)?),
"write_value" => {
write_value = Some(meta_name_value_to_parse(name_value)?);
}
_ => {
return Err(Error::new(
name_value.span(),
"unrecognized attribute",
))
}
}
}
if let Some(typ) = typ {
attribs.tag = Some(Tag::Prepend { typ, write_value });
} else {
return Err(Error::new(list.span(), "Tag lacks type."));
}
let mut tag = None;
let mut tag_type = None;
let mut tag_value = None;

for attr in attrs {
if attr.path().is_ident("protocol") {
attr.parse_nested_meta(|meta| {
if meta.path.is_ident("flexible_array_member") {
attribs.flexible_array_member = true;
} else if meta.path.is_ident("discriminant_type") {
attribs.discriminant_type = Some(meta.value()?.parse()?);
} else if meta.path.is_ident("discriminant") {
attribs.discriminant = Some(meta.value()?.parse()?);
} else if meta.path.is_ident("ctx") {
attribs.ctx = Some(meta.value()?.parse()?);
} else if meta.path.is_ident("ctx_bounds") {
attribs.ctx_bounds =
Some(Punctuated::parse_separated_nonempty(meta.value()?)?);
} else if meta.path.is_ident("bits") {
attribs.bits = Some(meta.value()?.parse()?);
} else if meta.path.is_ident("write_value") {
attribs.write_value = Some(meta.value()?.parse()?);
} else if meta.path.is_ident("tag") {
tag = Some(meta.value()?.parse()?);
} else if meta.path.is_ident("tag_type") {
tag_type = Some(meta.value()?.parse()?);
} else if meta.path.is_ident("tag_value") {
tag_value = Some(meta.value()?.parse()?);
} else {
return Err(meta.error("unrecognized protocol"));
}
_ => return Err(Error::new(meta_list.span(), "unrecognised attribute")),
};
Ok(())
})?;
}
}
Ok(attribs)
}
}

fn meta_name_value_to_parse<T: syn::parse::Parse>(name_value: &syn::MetaNameValue) -> Result<T> {
match name_value.lit {
syn::Lit::Str(ref s) => syn::parse_str::<T>(s.value().as_str())
.map_err(|e| Error::new(name_value.span(), format!("Failed to parse: {e}"))),

_ => Err(Error::new(name_value.span(), "Expected string")),
}
}

fn meta_name_value_to_u32(name_value: &syn::MetaNameValue) -> Result<u32> {
match name_value.lit {
syn::Lit::Int(ref i) => i
.base10_parse()
.map_err(|e| Error::new(name_value.span(), format!("Failed to parse u32: {e}"))),
_ => Err(Error::new(name_value.span(), "Expected integer")),
}
}
match (tag, tag_type, tag_value) {
(Some(tag), None, None) => attribs.tag = Some(Tag::External(tag)),
(None, Some(tag_type), tag_value) => {
attribs.tag = Some(Tag::Prepend {
typ: tag_type,
write_value: tag_value,
})
}
(None, None, None) => {}
_ => return Err(Error::new(attrs[0].span(), "TODO")),
}

fn meta_name_value_to_punctuated<T: syn::parse::Parse, P: syn::parse::Parse>(
name_value: &syn::MetaNameValue,
) -> Result<Punctuated<T, P>> {
match name_value.lit {
syn::Lit::Str(ref s) => Punctuated::parse_terminated
.parse_str(s.value().as_str())
.map_err(|e| Error::new(name_value.span(), format!("Failed to parse: {e}"))),
_ => Err(Error::new(name_value.span(), "Expected string")),
Ok(attribs)
}
}
10 changes: 5 additions & 5 deletions bin-proto-derive/src/codegen/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::{attr::Attrs, codegen, plan};
use proc_macro2::{Span, TokenStream};

pub fn read_discriminant(attribs: &Attrs) -> TokenStream {
if let Some(bits) = attribs.bits {
if let Some(bits) = &attribs.bits {
quote!(::bin_proto::BitFieldRead::read(__io_reader, __byte_order, __ctx, #bits))
} else {
quote!(::bin_proto::ProtocolRead::read(
Expand All @@ -14,7 +14,7 @@ pub fn read_discriminant(attribs: &Attrs) -> TokenStream {
}

pub fn write_discriminant(attribs: &Attrs) -> TokenStream {
let write_tag = if let Some(bits) = attribs.bits {
let write_tag = if let Some(bits) = &attribs.bits {
quote!(::bin_proto::BitFieldWrite::write(&__tag, __io_writer, __byte_order, __ctx, #bits))
} else {
quote!(::bin_proto::ProtocolWrite::write(
Expand Down Expand Up @@ -61,10 +61,10 @@ pub fn variant_discriminant(plan: &plan::Enum, attribs: &Attrs) -> TokenStream {
let variant_name = &variant.ident;
let fields_pattern = bind_fields_pattern(variant_name, &variant.fields);
let discriminant_expr = &variant.discriminant_value;
let write_variant = if let Some(field_width) = attribs.bits {
let write_variant = if let Some(field_width) = &attribs.bits {
let error_message = format!(
"Discriminant for variant '{}' does not fit in bitfield with width {}.",
variant.ident, field_width
"Discriminant for variant '{}' does not fit in bitfield.",
variant.ident
);
quote!(
const _: () = ::std::assert!(#discriminant_expr < (1 as #discriminant_ty) << #field_width, #error_message);
Expand Down
6 changes: 3 additions & 3 deletions bin-proto/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bin-proto"
version = "0.5.0"
version = "0.6.0"
authors = [
"Wojciech Graj <wojtek.graj.2004@gmail.com>",
"Dylan McKay <me@dylanmckay.io>"
Expand All @@ -20,6 +20,6 @@ default = ["derive"]
derive = ["bin-proto-derive"]

[dependencies]
bin-proto-derive = { version = "0.5.0", path = "../bin-proto-derive", optional = true }
bin-proto-derive = { version = "0.6.0", path = "../bin-proto-derive", optional = true }
bitstream-io = "2.3.0"
thiserror = "1.0.61"
thiserror = "2.0.2"
30 changes: 15 additions & 15 deletions bin-proto/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
//! ```
//! # use bin_proto::{ProtocolRead, ProtocolWrite, ProtocolNoCtx};
//! #[derive(Debug, ProtocolRead, ProtocolWrite, PartialEq)]
//! #[protocol(discriminant_type = "u8")]
//! #[protocol(discriminant_type = u8)]
//! #[protocol(bits = 4)]
//! enum E {
//! V1 = 1,
//! #[protocol(discriminant = "4")]
//! #[protocol(discriminant = 4)]
//! V4,
//! }
//!
Expand All @@ -22,11 +22,11 @@
//! #[protocol(bits = 3)]
//! bitfield: u8,
//! enum_: E,
//! #[protocol(write_value = "self.arr.len() as u8")]
//! #[protocol(write_value = self.arr.len() as u8)]
//! arr_len: u8,
//! #[protocol(tag = "arr_len as usize")]
//! #[protocol(tag = arr_len as usize)]
//! arr: Vec<u8>,
//! #[protocol(tag(type = "u16", write_value = "self.prefixed_arr.len() as u16"))]
//! #[protocol(tag_type = u16, tag_value = self.prefixed_arr.len() as u16)]
//! prefixed_arr: Vec<u8>,
//! #[protocol(flexible_array_member)]
//! read_to_end: Vec<u8>,
Expand Down Expand Up @@ -85,7 +85,7 @@ pub use self::tagged::{TaggedRead, UntaggedWrite};
/// ```
/// # use bin_proto::{ProtocolRead, ProtocolWrite};
/// #[derive(ProtocolRead, ProtocolWrite)]
/// #[protocol(discriminant_type = "u8")]
/// #[protocol(discriminant_type = u8)]
/// enum Example {
/// Variant1 = 1,
/// Variant5 = 5,
Expand All @@ -99,9 +99,9 @@ pub use self::tagged::{TaggedRead, UntaggedWrite};
/// ```
/// # use bin_proto::{ProtocolRead, ProtocolWrite};
/// #[derive(ProtocolRead, ProtocolWrite)]
/// #[protocol(discriminant_type = "u8")]
/// #[protocol(discriminant_type = u8)]
/// enum Example {
/// #[protocol(discriminant = "1")]
/// #[protocol(discriminant = 1)]
/// Variant1,
/// Variant5 = 5,
/// }
Expand Down Expand Up @@ -150,7 +150,7 @@ pub use self::tagged::{TaggedRead, UntaggedWrite};
/// pub struct WithElementsLength {
/// pub count: u32,
/// pub foo: bool,
/// #[protocol(tag = "count as usize")]
/// #[protocol(tag = count as usize)]
/// pub data: Vec<u32>,
/// }
/// ```
Expand All @@ -170,7 +170,7 @@ pub use self::tagged::{TaggedRead, UntaggedWrite};
/// # use bin_proto::{ProtocolRead, ProtocolWrite};
/// #[derive(ProtocolRead, ProtocolWrite)]
/// pub struct WithElementsLength {
/// #[protocol(tag(type = "u16", write_value = "self.data.len() as u16"))]
/// #[protocol(tag_type = u16, tag_value = self.data.len() as u16)]
/// pub data: Vec<u32>,
/// }
/// ```
Expand All @@ -186,10 +186,10 @@ pub use self::tagged::{TaggedRead, UntaggedWrite};
/// # use bin_proto::{ProtocolRead, ProtocolWrite};
/// #[derive(ProtocolRead, ProtocolWrite)]
/// pub struct WithElementsLengthAuto {
/// #[protocol(write_value = "self.data.len() as u32")]
/// #[protocol(write_value = self.data.len() as u32)]
/// pub count: u32,
/// pub foo: bool,
/// #[protocol(tag = "count as usize")]
/// #[protocol(tag = count as usize)]
/// pub data: Vec<u32>,
/// }
/// ```
Expand Down Expand Up @@ -231,7 +231,7 @@ pub use self::tagged::{TaggedRead, UntaggedWrite};
/// }
///
/// #[derive(ProtocolRead, ProtocolWrite)]
/// #[protocol(ctx = "Ctx")]
/// #[protocol(ctx = Ctx)]
/// pub struct WithCtx(NeedsCtx);
///
/// WithCtx(NeedsCtx)
Expand All @@ -243,7 +243,7 @@ pub use self::tagged::{TaggedRead, UntaggedWrite};
/// # use bin_proto::{ProtocolRead, ProtocolWrite};
/// # use std::marker::PhantomData;
/// #[derive(ProtocolRead, ProtocolWrite)]
/// #[protocol(ctx = "Ctx")]
/// #[protocol(ctx = Ctx)]
/// pub struct NestedProtocol<Ctx, A: ProtocolRead<Ctx> + ProtocolWrite<Ctx>>(A, PhantomData<Ctx>);
/// ```
///
Expand Down Expand Up @@ -283,7 +283,7 @@ pub use self::tagged::{TaggedRead, UntaggedWrite};
/// }
///
/// #[derive(ProtocolRead, ProtocolWrite)]
/// #[protocol(ctx_bounds = "CtxTrait")]
/// #[protocol(ctx_bounds = CtxTrait)]
/// pub struct WithCtx(NeedsCtx);
/// ```
#[cfg(feature = "derive")]
Expand Down
4 changes: 2 additions & 2 deletions bin-proto/tests/ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ impl<Ctx: CtxTrait> ProtocolWrite<Ctx> for CtxCheck {
}

#[derive(Debug, ProtocolRead, ProtocolWrite)]
#[protocol(ctx = "CtxStruct")]
#[protocol(ctx = CtxStruct)]
struct CtxCheckStructWrapper(CtxCheck);

#[derive(Debug, ProtocolRead, ProtocolWrite)]
#[protocol(ctx_bounds = "CtxTrait")]
#[protocol(ctx_bounds = CtxTrait)]
struct CtxCheckTraitWrapper(CtxCheck);

#[test]
Expand Down
Loading

0 comments on commit c11be67

Please sign in to comment.