use syn::{Attribute, Expr, Field, GenericArgument, LitInt, PathArguments, Result, Type};
pub fn transform_expr_for_self(expr: &Expr) -> Expr {
match expr {
Expr::Path(expr_path)
if expr_path.path.segments.len() == 1 && expr_path.qself.is_none() =>
{
let ident = &expr_path.path.segments[0].ident;
syn::parse_quote!(self.#ident)
}
Expr::Binary(expr_bin) => {
let left = transform_expr_for_self(&expr_bin.left);
let right = transform_expr_for_self(&expr_bin.right);
let op = &expr_bin.op;
syn::parse_quote!(#left #op #right)
}
Expr::Paren(expr_paren) => {
let inner = transform_expr_for_self(&expr_paren.expr);
syn::parse_quote!((#inner))
}
_ => expr.clone(),
}
}
pub fn transform_expr_for_local(expr: &Expr) -> Expr {
expr.clone()
}
pub fn is_vec_u8(field: &Field) -> bool {
match &field.ty {
Type::Path(type_path) => {
if type_path.path.segments.len() != 1 {
return false;
}
let segment = &type_path.path.segments[0];
if segment.ident != "Vec" {
return false;
}
match &segment.arguments {
PathArguments::AngleBracketed(args) => {
args.args.len() == 1
&& args.args.iter().any(|arg| {
if let GenericArgument::Type(Type::Path(type_path)) = arg {
type_path.path.segments.len() == 1
&& type_path.path.segments[0].ident == "u8"
} else {
false
}
})
}
_ => false,
}
}
_ => false,
}
}
#[derive(Default)]
pub struct FieldAttrs {
pub varint32: bool,
pub varint64: bool,
pub json: bool,
pub fixed_length: Option<Box<Expr>>,
pub remaining: bool,
}
#[derive(Default)]
pub struct TypeAttrs {
pub tagged: bool,
pub varint32: bool,
pub varint64: bool,
}
#[derive(Default)]
pub struct VariantAttrs {
pub packet_id: Option<u8>,
}
pub fn parse_field_attrs(attrs: &[Attribute]) -> Result<FieldAttrs> {
let mut field_attrs = FieldAttrs::default();
for attr in attrs {
if !attr.path().is_ident("codec") {
continue;
}
attr.parse_nested_meta(|meta| {
if meta.path.is_ident("varint32") {
field_attrs.varint32 = true;
Ok(())
} else if meta.path.is_ident("varint64") {
field_attrs.varint64 = true;
Ok(())
} else if meta.path.is_ident("json") {
field_attrs.json = true;
Ok(())
} else if meta.path.is_ident("fixed_length") {
let _eq: syn::token::Eq = meta.input.parse()?;
let expr: Expr = meta.input.parse()?;
field_attrs.fixed_length = Some(Box::new(expr));
Ok(())
} else if meta.path.is_ident("remaining") {
field_attrs.remaining = true;
Ok(())
} else {
Err(meta.error("unknown field codec attribute"))
}
})?;
}
Ok(field_attrs)
}
pub fn parse_type_attrs(attrs: &[Attribute]) -> Result<TypeAttrs> {
let mut type_attrs = TypeAttrs::default();
for attr in attrs {
if !attr.path().is_ident("codec") {
continue;
}
attr.parse_nested_meta(|meta| {
if meta.path.is_ident("tagged") {
type_attrs.tagged = true;
Ok(())
} else if meta.path.is_ident("varint32") {
type_attrs.varint32 = true;
Ok(())
} else if meta.path.is_ident("varint64") {
type_attrs.varint64 = true;
Ok(())
} else {
Err(meta.error("unknown type codec attribute"))
}
})?;
}
Ok(type_attrs)
}
pub fn parse_variant_attrs(attrs: &[Attribute]) -> Result<VariantAttrs> {
let mut variant_attrs = VariantAttrs::default();
for attr in attrs {
if !attr.path().is_ident("codec") {
continue;
}
attr.parse_nested_meta(|meta| {
if meta.path.is_ident("packet_id") {
let _eq: syn::token::Eq = meta.input.parse()?;
let lit: LitInt = meta.input.parse()?;
variant_attrs.packet_id = Some(lit.base10_parse()?);
Ok(())
} else {
Err(meta.error("unknown variant codec attribute"))
}
})?;
}
Ok(variant_attrs)
}
pub fn parse_repr_type(attrs: &[Attribute]) -> Result<Option<String>> {
for attr in attrs {
if !attr.path().is_ident("repr") {
continue;
}
let mut repr_type = None;
attr.parse_nested_meta(|meta| {
if meta.path.is_ident("u8") {
repr_type = Some("u8".to_string());
Ok(())
} else if meta.path.is_ident("i32") {
repr_type = Some("i32".to_string());
Ok(())
} else if meta.path.is_ident("u32") {
repr_type = Some("u32".to_string());
Ok(())
} else if meta.path.is_ident("i64") {
repr_type = Some("i64".to_string());
Ok(())
} else if meta.path.is_ident("u64") {
repr_type = Some("u64".to_string());
Ok(())
} else if meta.path.is_ident("i16") {
repr_type = Some("i16".to_string());
Ok(())
} else if meta.path.is_ident("u16") {
repr_type = Some("u16".to_string());
Ok(())
} else {
Err(meta.error("unsupported repr type for codec"))
}
})?;
return Ok(repr_type);
}
Ok(None)
}