use syn::{Attribute, Error, Lit, Meta, NestedMeta, Result};
#[derive(Default, Clone)]
pub(crate) enum EnumHead {
#[default]
Unset,
Transparent,
Head(String),
}
#[derive(Default)]
pub(crate) struct ContainerAttrs {
pub symbol: Option<String>,
pub enum_head: EnumHead,
pub key_processor: Option<String>,
}
pub(crate) fn process_key(key: &str, processor: Option<&str>) -> String {
match processor {
Some("CamelCase") => key
.split('_')
.map(|seg| {
let mut chars = seg.chars();
match chars.next() {
Some(first) => {
first.to_uppercase().collect::<String>() + chars.as_str()
},
None => String::new(),
}
})
.collect(),
_ => key.to_string(),
}
}
#[derive(Default)]
pub(crate) struct FieldAttrs {
pub rename: Option<String>,
}
pub(crate) fn parse_container_attrs(attrs: &[Attribute]) -> Result<ContainerAttrs> {
let mut out = ContainerAttrs::default();
for attr in attrs {
if !attr.path.is_ident("wolfram") {
continue;
}
let meta = attr.parse_meta()?;
let list = match meta {
Meta::List(list) => list,
_ => return Err(Error::new_spanned(attr, "expected `#[wolfram(...)]`")),
};
for nested in list.nested {
match nested {
NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("symbol") => {
out.symbol = Some(string_lit(&nv.lit)?);
},
NestedMeta::Meta(Meta::NameValue(nv))
if nv.path.is_ident("enum_head") =>
{
out.enum_head = match &nv.lit {
Lit::Str(s) => EnumHead::Head(s.value()),
Lit::Bool(b) if !b.value => EnumHead::Transparent,
other => {
return Err(Error::new_spanned(
other,
"enum_head expects a head string (e.g. \"System`Failure\") or `false` for no head",
))
},
};
},
NestedMeta::Meta(Meta::NameValue(nv))
if nv.path.is_ident("key_processor") =>
{
out.key_processor = Some(string_lit(&nv.lit)?);
},
other => {
return Err(Error::new_spanned(
other,
"unknown `#[wolfram(...)]` option; expected `symbol`, `enum_head`, or `key_processor`",
));
},
}
}
}
Ok(out)
}
pub(crate) fn parse_field_attrs(attrs: &[Attribute]) -> Result<FieldAttrs> {
let mut out = FieldAttrs::default();
for attr in attrs {
if !attr.path.is_ident("wolfram") {
continue;
}
let meta = attr.parse_meta()?;
let list = match meta {
Meta::List(list) => list,
_ => return Err(Error::new_spanned(attr, "expected `#[wolfram(...)]`")),
};
for nested in list.nested {
match nested {
NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("rename") => {
out.rename = Some(string_lit(&nv.lit)?);
},
other => {
return Err(Error::new_spanned(
other,
"unknown `#[wolfram(...)]` option here; expected `rename = \"...\"`",
));
},
}
}
}
Ok(out)
}
fn string_lit(lit: &Lit) -> Result<String> {
match lit {
Lit::Str(s) => Ok(s.value()),
other => Err(Error::new_spanned(other, "expected a string literal")),
}
}