use crate::attr::FieldAttr;
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::punctuated::Punctuated;
use syn::token::Comma;
use syn::{
Attribute, Error, Expr, ExprLit, Field, Fields, Ident, Lit, LitInt, Result,
TypePath, Variant,
};
use proc_macro_crate::{crate_name, FoundCrate};
pub(crate) fn protopuffer_crate() -> Result<TokenStream> {
let name = crate_name("protopuffer")
.map_err(|e| Error::new(Span::call_site(), e))?;
Ok(match name {
FoundCrate::Itself => quote!(protopuffer),
FoundCrate::Name(n) => {
let ident = Ident::new(&n, Span::call_site());
quote!(#ident)
}
})
}
pub(crate) fn repr_as_i32(attrs: Vec<Attribute>) -> Result<bool> {
let mut repr_as = None;
for attr in attrs {
if !attr.path().is_ident("repr") {
continue;
}
let ty: TypePath = attr.parse_args()?;
repr_as = Some(ty);
}
match repr_as {
Some(path) => {
if !path.path.is_ident("i32") {
return Err(Error::new_spanned(path, "expected i32"));
}
Ok(true)
}
None => Ok(false),
}
}
pub(crate) fn variants_no_fields(
variants: Punctuated<Variant, Comma>,
) -> Result<(Vec<(LitInt, Ident)>, (LitInt, Ident))> {
let mut variants: Vec<_> = variants
.into_iter()
.map(|v| {
let fieldnum_expr = v
.discriminant
.ok_or_else(|| {
Error::new_spanned(
&v.ident,
"needs to have a field number `Ident = x`",
)
})?
.1;
let fieldnum = match fieldnum_expr {
Expr::Lit(ExprLit {
lit: Lit::Int(int), ..
}) => int,
e => return Err(Error::new_spanned(e, "expected = int")),
};
let ident = v.ident;
if !matches!(v.fields, Fields::Unit) {
return Err(Error::new_spanned(v.fields, "no fields allowed"));
}
Ok((fieldnum, ident))
})
.collect::<Result<_>>()?;
let default_variant = variants
.iter()
.position(|(num, _)| num.base10_digits() == "0")
.ok_or_else(|| {
Error::new(Span::call_site(), "a fields needs to be the default")
})?;
let default_variant = variants.remove(default_variant);
let has_still_default =
variants.iter().any(|(num, _)| num.base10_digits() == "0");
if has_still_default {
Err(Error::new(
Span::call_site(),
"only one variant can be default = \
0",
))
} else {
Ok((variants, default_variant))
}
}
pub(crate) fn variants_with_fields(
variants: Punctuated<Variant, Comma>,
) -> Result<Vec<(FieldAttr, Ident, Option<Field>)>> {
let v = variants
.into_iter()
.map(|v| {
let attr = FieldAttr::from_attrs(&v.attrs)?;
let ident = v.ident;
let field = match v.fields {
Fields::Unnamed(unnamed) => {
if unnamed.unnamed.len() != 1 {
return Err(Error::new_spanned(
unnamed,
"only one unnamed field allowed",
));
}
let field = unnamed.unnamed.into_iter().next().unwrap();
Some(field)
}
Fields::Unit => None,
Fields::Named(n) => {
return Err(Error::new_spanned(
n,
"named fields not supported",
))
}
};
Ok((attr, ident, field))
})
.collect::<Result<Vec<_>>>()?;
let defaults = v
.iter()
.filter(|(attr, _, _)| attr.default.is_some())
.count();
if defaults == 0 {
Err(Error::new(
Span::call_site(),
"one variant needs to the \
default have #[field(num, default)]",
))
} else if defaults > 1 {
Err(Error::new(
Span::call_site(),
"only one variant can be the \
default",
))
} else {
Ok(v)
}
}