use proc_macro2::Span;
use syn::{
Ident, LitBool, LitStr, Result, Token, Visibility,
parse::{Parse, ParseStream},
};
use crate::rename::RenameRule;
pub struct MacroInput {
pub path: LitStr,
pub name: Ident,
pub vis: Visibility,
pub data_vis: Option<Visibility>,
pub embedded: Option<bool>,
pub rename_all: Option<RenameRule>,
}
enum KvPair {
Path(LitStr),
Name(Ident),
Vis(Visibility),
DataVis(Visibility),
Embedded(bool),
RenameAll(RenameRule),
}
fn parse_vis(input: ParseStream<'_>) -> Result<Visibility> {
if input.peek(Token![pub]) {
input.parse::<Visibility>()
} else {
let ident: Ident = input.parse()?;
if ident == "private" {
Ok(Visibility::Inherited)
} else {
Err(syn::Error::new(
ident.span(),
format!(
"expected `pub`, `pub(crate)`, `pub(super)`, `pub(self)`, or `private`; \
found `{ident}`"
),
))
}
}
}
impl Parse for KvPair {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let key: Ident = input.parse()?;
input.parse::<Token![=]>()?;
match key.to_string().as_str() {
"path" => Ok(KvPair::Path(input.parse()?)),
"name" => {
if input.peek(LitStr) {
let lit: LitStr = input.parse()?;
Ok(KvPair::Name(Ident::new(&lit.value(), lit.span())))
} else {
Ok(KvPair::Name(input.parse()?))
}
},
"vis" => Ok(KvPair::Vis(parse_vis(input)?)),
"data_vis" => Ok(KvPair::DataVis(parse_vis(input)?)),
"embedded" => {
let b: LitBool = input.parse()?;
Ok(KvPair::Embedded(b.value()))
},
"rename_all" => {
let lit: LitStr = input.parse()?;
Ok(KvPair::RenameAll(RenameRule::from_lit(&lit)?))
},
other => Err(syn::Error::new(
key.span(),
format!(
"unknown key `{other}`; \
expected `path`, `name`, `vis`, `data_vis`, `embedded`, or `rename_all`"
),
)),
}
}
}
impl Parse for MacroInput {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let mut path: Option<LitStr> = None;
let mut name: Option<Ident> = None;
let mut vis: Option<Visibility> = None;
let mut data_vis: Option<Visibility> = None;
let mut embedded: Option<bool> = None;
let mut rename_all: Option<RenameRule> = None;
while !input.is_empty() {
let kv: KvPair = input.parse()?;
let _ = input.parse::<Token![,]>();
match kv {
KvPair::Path(v) => {
if path.replace(v).is_some() {
return Err(syn::Error::new(Span::call_site(), "duplicate `path` key"));
}
},
KvPair::Name(v) => {
if name.replace(v).is_some() {
return Err(syn::Error::new(Span::call_site(), "duplicate `name` key"));
}
},
KvPair::Vis(v) => {
if vis.replace(v).is_some() {
return Err(syn::Error::new(Span::call_site(), "duplicate `vis` key"));
}
},
KvPair::DataVis(v) => {
if data_vis.replace(v).is_some() {
return Err(syn::Error::new(
Span::call_site(),
"duplicate `data_vis` key",
));
}
},
KvPair::Embedded(v) => {
if embedded.replace(v).is_some() {
return Err(syn::Error::new(
Span::call_site(),
"duplicate `embedded` key",
));
}
},
KvPair::RenameAll(v) => {
if rename_all.replace(v).is_some() {
return Err(syn::Error::new(
Span::call_site(),
"duplicate `rename_all` key",
));
}
},
}
}
let path =
path.ok_or_else(|| syn::Error::new(Span::call_site(), "missing required key `path`"))?;
let name =
name.ok_or_else(|| syn::Error::new(Span::call_site(), "missing required key `name`"))?;
let vis = vis.unwrap_or(Visibility::Public(Token)));
Ok(MacroInput {
path,
name,
vis,
data_vis,
embedded,
rename_all,
})
}
}
pub struct AttrInput {
pub path: LitStr,
pub data_vis: Option<Visibility>,
pub embedded: Option<bool>,
pub rename_all: Option<RenameRule>,
}
impl Parse for AttrInput {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let mut path: Option<LitStr> = None;
let mut data_vis: Option<Visibility> = None;
let mut embedded: Option<bool> = None;
let mut rename_all: Option<RenameRule> = None;
while !input.is_empty() {
let key: Ident = input.parse()?;
input.parse::<Token![=]>()?;
match key.to_string().as_str() {
"path" => {
let v: LitStr = input.parse()?;
if path.replace(v).is_some() {
return Err(syn::Error::new(Span::call_site(), "duplicate `path` key"));
}
},
"data_vis" => {
let v = parse_vis(input)?;
if data_vis.replace(v).is_some() {
return Err(syn::Error::new(
Span::call_site(),
"duplicate `data_vis` key",
));
}
},
"embedded" => {
let b: LitBool = input.parse()?;
if embedded.replace(b.value()).is_some() {
return Err(syn::Error::new(
Span::call_site(),
"duplicate `embedded` key",
));
}
},
"rename_all" => {
let lit: LitStr = input.parse()?;
if rename_all.replace(RenameRule::from_lit(&lit)?).is_some() {
return Err(syn::Error::new(
Span::call_site(),
"duplicate `rename_all` key",
));
}
},
other => {
return Err(syn::Error::new(
key.span(),
format!(
"unknown key `{other}`; \
expected `path`, `data_vis`, `embedded`, or `rename_all`"
),
));
},
}
let _ = input.parse::<Token![,]>();
}
let path =
path.ok_or_else(|| syn::Error::new(Span::call_site(), "missing required key `path`"))?;
Ok(AttrInput {
path,
data_vis,
embedded,
rename_all,
})
}
}