use syn::spanned::Spanned;
use syn::{Attribute, Expr, Lit, Meta, Token, punctuated::Punctuated};
#[derive(Debug)]
pub(crate) struct Item {
pub(crate) key: String,
pub(crate) value: Option<Expr>,
pub(crate) span: proc_macro2::Span,
}
impl Item {
pub(crate) fn str_value(&self) -> syn::Result<String> {
match &self.value {
Some(Expr::Lit(syn::ExprLit {
lit: Lit::Str(s), ..
})) => Ok(s.value()),
_ => Err(syn::Error::new(
self.span,
format!("`{}` expects a string literal", self.key),
)),
}
}
pub(crate) fn bool_value(&self) -> syn::Result<bool> {
match &self.value {
None => Ok(true),
Some(Expr::Lit(syn::ExprLit {
lit: Lit::Bool(b), ..
})) => Ok(b.value),
_ => Err(syn::Error::new(
self.span,
format!("`{}` expects a boolean", self.key),
)),
}
}
}
pub(crate) fn parse(attrs: &[Attribute]) -> syn::Result<Vec<Item>> {
let mut items = Vec::new();
for attr in attrs {
if !attr.path().is_ident("turso") {
continue;
}
let nested = attr.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
for meta in nested {
let span = meta_span(&meta);
match meta {
Meta::Path(p) => items.push(Item {
key: path_name(&p)?,
value: None,
span,
}),
Meta::NameValue(nv) => items.push(Item {
key: path_name(&nv.path)?,
value: Some(nv.value),
span,
}),
Meta::List(l) => {
return Err(syn::Error::new(
l.path.span(),
"nested lists are not supported",
));
}
}
}
}
Ok(items)
}
fn meta_span(meta: &Meta) -> proc_macro2::Span {
meta.path().span()
}
fn path_name(path: &syn::Path) -> syn::Result<String> {
path.get_ident()
.map(ToString::to_string)
.ok_or_else(|| syn::Error::new(path.span(), "expected an identifier"))
}