use syn::{Expr, Lit, Meta, Path};
#[derive(Debug, Default, Clone)]
pub struct ComponentDocAttrs {
pub section: Option<String>,
pub section_priority: Option<u16>,
pub category: Option<String>,
pub category_priority: Option<u16>,
pub category_default_collapsed: Option<bool>,
pub group: Option<String>,
pub group_priority: Option<u16>,
pub nav_item: bool,
pub preview_slug: Option<String>,
pub preview_label: Option<String>,
pub preview_icon: Option<Path>,
pub preview_import: Option<Path>,
pub props_import: Option<Path>,
pub preview_manual: bool,
}
impl ComponentDocAttrs {
pub fn parse(metas: &syn::punctuated::Punctuated<Meta, syn::Token![,]>) -> syn::Result<Self> {
let mut attrs = Self::default();
for meta in metas {
match meta {
Meta::NameValue(nv) if nv.path.is_ident("section") => {
attrs.section = Some(lit_to_string(&nv.value)?);
}
Meta::NameValue(nv) if nv.path.is_ident("section_priority") => {
attrs.section_priority = Some(lit_to_u16(&nv.value)?);
}
Meta::NameValue(nv) if nv.path.is_ident("category") => {
attrs.category = Some(lit_to_string(&nv.value)?);
}
Meta::NameValue(nv) if nv.path.is_ident("category_priority") => {
attrs.category_priority = Some(lit_to_u16(&nv.value)?);
}
Meta::NameValue(nv) if nv.path.is_ident("category_default_collapsed") => {
attrs.category_default_collapsed = Some(lit_to_bool(&nv.value)?);
}
Meta::NameValue(nv) if nv.path.is_ident("group") => {
attrs.group = Some(lit_to_string(&nv.value)?);
}
Meta::NameValue(nv) if nv.path.is_ident("group_priority") => {
attrs.group_priority = Some(lit_to_u16(&nv.value)?);
}
Meta::NameValue(nv) if nv.path.is_ident("nav_item") => {
attrs.nav_item = lit_to_bool(&nv.value)?;
}
Meta::Path(path) if path.is_ident("nav_item") => {
attrs.nav_item = true;
}
Meta::NameValue(nv) if nv.path.is_ident("preview_slug") => {
attrs.preview_slug = Some(lit_to_string(&nv.value)?);
}
Meta::NameValue(nv) if nv.path.is_ident("preview_label") => {
attrs.preview_label = Some(lit_to_string(&nv.value)?);
}
Meta::NameValue(nv) if nv.path.is_ident("preview") => {
let mode = lit_to_string(&nv.value)?;
attrs.preview_manual = mode == "manual";
}
Meta::NameValue(nv) if nv.path.is_ident("preview_icon") => {
attrs.preview_icon = Some(expr_to_path(&nv.value)?);
}
Meta::NameValue(nv) if nv.path.is_ident("preview_import") => {
attrs.preview_import = Some(expr_to_path(&nv.value)?);
}
Meta::NameValue(nv) if nv.path.is_ident("props_import") => {
attrs.props_import = Some(expr_to_path(&nv.value)?);
}
Meta::Path(path) if path.is_ident("preview") => {
attrs.preview_manual = false;
}
other => {
return Err(syn::Error::new_spanned(
other,
"unknown `#[component_doc]` attribute",
));
}
}
}
Ok(attrs)
}
pub fn is_preview_enabled(&self) -> bool {
!self.preview_manual && self.preview_slug.is_some()
}
}
fn lit_to_string(expr: &Expr) -> syn::Result<String> {
match expr {
Expr::Lit(expr_lit) => match &expr_lit.lit {
Lit::Str(s) => Ok(s.value()),
other => Err(syn::Error::new_spanned(other, "expected string literal")),
},
other => Err(syn::Error::new_spanned(other, "expected string literal")),
}
}
fn lit_to_u16(expr: &Expr) -> syn::Result<u16> {
match expr {
Expr::Lit(expr_lit) => match &expr_lit.lit {
Lit::Int(int) => int.base10_parse(),
other => Err(syn::Error::new_spanned(other, "expected integer literal")),
},
other => Err(syn::Error::new_spanned(other, "expected integer literal")),
}
}
fn lit_to_bool(expr: &Expr) -> syn::Result<bool> {
match expr {
Expr::Lit(expr_lit) => match &expr_lit.lit {
Lit::Bool(value) => Ok(value.value()),
other => Err(syn::Error::new_spanned(other, "expected bool literal")),
},
other => Err(syn::Error::new_spanned(other, "expected bool literal")),
}
}
fn expr_to_path(expr: &Expr) -> syn::Result<Path> {
match expr {
Expr::Path(expr_path) => Ok(expr_path.path.clone()),
other => Err(syn::Error::new_spanned(other, "expected type path")),
}
}