use std::collections::{HashMap, HashSet};
use std::fmt::{self, Debug, Formatter};
use syn::parse::Parser;
use syn::parse_quote::ParseQuote;
use syn::{
Attribute, Lit, LitBool, LitByteStr, LitChar, LitFloat, LitInt, LitStr, Meta, MetaNameValue,
Path, Type,
};
use crate::{ArgValue, ArgValueReq, AttrReq, Error, MetaArg, MetaArgList, MetaArgNameValue};
#[derive(Clone, Debug)]
pub enum Attr {
Singular(SingularAttr),
Parametrized(ParametrizedAttr),
}
#[derive(Clone, Debug)]
pub struct SingularAttr {
pub name: String,
pub value: ArgValue,
}
#[derive(Clone)]
pub struct ParametrizedAttr {
pub name: String,
pub args: HashMap<String, ArgValue>,
pub paths: Vec<Path>,
pub string: Option<LitStr>,
pub bytes: Option<LitByteStr>,
pub chars: Vec<LitChar>,
pub integers: Vec<LitInt>,
pub floats: Vec<LitFloat>,
pub bool: Option<LitBool>,
}
impl Attr {
pub fn with(name: impl ToString + AsRef<str>, attrs: &[Attribute]) -> Result<Self, Error> {
SingularAttr::with(name.to_string(), attrs)
.map(Attr::Singular)
.or_else(|_| ParametrizedAttr::with(name, attrs).map(Attr::Parametrized))
}
pub fn from_attribute(attr: &Attribute) -> Result<Self, Error> {
SingularAttr::from_attribute(attr)
.map(Attr::Singular)
.or_else(|_| ParametrizedAttr::from_attribute(attr).map(Attr::Parametrized))
}
#[inline]
pub fn try_singular(self) -> Result<SingularAttr, Error> {
match self {
Attr::Singular(attr) => Ok(attr),
Attr::Parametrized(attr) => Err(Error::SingularAttrRequired(attr.name)),
}
}
#[inline]
pub fn try_parametrized(self) -> Result<ParametrizedAttr, Error> {
match self {
Attr::Singular(attr) => Err(Error::ParametrizedAttrRequired(attr.name)),
Attr::Parametrized(attr) => Ok(attr),
}
}
#[inline]
pub fn name(&self) -> &str {
match self {
Attr::Singular(attr) => &attr.name,
Attr::Parametrized(attr) => &attr.name,
}
}
#[inline]
pub fn arg_value(&self) -> Result<ArgValue, Error> {
match self {
Attr::Singular(attr) => Ok(attr.value.clone()),
Attr::Parametrized(attr) => Err(Error::ParametrizedAttrHasNoValue(attr.name.clone())),
}
}
#[inline]
pub fn literal_value(&self) -> Result<Lit, Error> { self.arg_value()?.to_literal_value() }
#[inline]
pub fn type_value(&self) -> Result<Type, Error> { self.arg_value()?.to_type_value() }
}
impl SingularAttr {
#[inline]
pub fn new(name: impl ToString) -> Self {
Self {
name: name.to_string(),
value: ArgValue::None,
}
}
pub fn with(name: impl ToString, attrs: &[Attribute]) -> Result<Self, Error> {
let name = name.to_string();
let mut filtered_attrs = attrs.iter().filter(|attr| attr.path.is_ident(&name));
let res = if let Some(attr) = filtered_attrs.next() {
SingularAttr::from_attribute(attr)
} else {
return Err(Error::SingularAttrRequired(name));
};
if filtered_attrs.count() > 0 {
return Err(Error::SingularAttrRequired(name));
}
res
}
#[inline]
pub fn with_literal(name: impl ToString, lit: Lit) -> Self {
Self {
name: name.to_string(),
value: ArgValue::Literal(lit),
}
}
#[inline]
pub fn with_type(name: impl ToString, ty: Type) -> Self {
Self {
name: name.to_string(),
value: ArgValue::Type(ty),
}
}
pub fn from_attribute(attr: &Attribute) -> Result<Self, Error> {
let ident = attr
.path
.get_ident()
.ok_or(Error::ArgNameMustBeIdent)?
.to_string();
match attr.parse_meta()? {
Meta::Path(_) => unreachable!(),
Meta::NameValue(MetaNameValue { lit, .. }) => {
Ok(SingularAttr::with_literal(ident, lit))
}
Meta::List(_) => Err(Error::SingularAttrRequired(ident)),
}
}
#[inline]
pub fn literal_value(&self) -> Result<Lit, Error> { self.value.to_literal_value() }
#[inline]
pub fn type_value(&self) -> Result<Type, Error> { self.value.to_type_value() }
pub fn merge(&mut self, other: Self) -> Result<(), Error> {
if self.name != other.name {
return Err(Error::NamesDontMatch(self.name.clone(), other.name));
}
match (&self.value, &other.value) {
(_, ArgValue::None) => {}
(ArgValue::None, _) => self.value = other.value,
(_, _) => return Err(Error::MultipleSingularValues(self.name.clone())),
}
Ok(())
}
#[inline]
pub fn merged(mut self, other: Self) -> Result<Self, Error> {
self.merge(other)?;
Ok(self)
}
#[inline]
pub fn enrich(&mut self, attr: &Attribute) -> Result<(), Error> {
self.merge(SingularAttr::from_attribute(attr)?)
}
#[inline]
pub fn enriched(mut self, attr: &Attribute) -> Result<Self, Error> {
self.enrich(attr)?;
Ok(self)
}
pub fn check(&mut self, req: ArgValueReq) -> Result<(), Error> {
req.check(&mut self.value, &self.name, &self.name)?;
Ok(())
}
#[inline]
pub fn checked(mut self, req: ArgValueReq) -> Result<Self, Error> {
self.check(req)?;
Ok(self)
}
}
impl ParametrizedAttr {
#[inline]
pub fn new(name: impl ToString) -> Self {
Self {
name: name.to_string(),
args: Default::default(),
paths: vec![],
string: None,
bytes: None,
chars: vec![],
integers: vec![],
floats: vec![],
bool: None,
}
}
pub fn with(name: impl ToString + AsRef<str>, attrs: &[Attribute]) -> Result<Self, Error> {
let mut me = ParametrizedAttr::new(name.to_string());
for attr in attrs.iter().filter(|attr| attr.path.is_ident(&name)) {
me.fuse(attr)?;
}
Ok(me)
}
pub fn from_attribute(attr: &Attribute) -> Result<Self, Error> {
let name = attr
.path
.get_ident()
.ok_or(Error::ArgNameMustBeIdent)?
.to_string();
ParametrizedAttr::new(name).fused(attr)
}
pub fn arg_value<T>(&self, name: &str) -> Result<T, Error>
where T: TryFrom<ArgValue, Error = Error> {
self.args
.get(name)
.ok_or(Error::ArgRequired {
attr: self.name.clone(),
arg: name.to_owned(),
})
.and_then(|a| a.clone().try_into())
}
pub fn unwrap_arg_value<T>(&self, name: &str) -> T
where T: TryFrom<ArgValue, Error = Error> {
self.args
.get(name)
.ok_or(Error::ArgRequired {
attr: self.name.clone(),
arg: name.to_owned(),
})
.and_then(|a| a.clone().try_into())
.expect("required attribute value is not present or has an invalid type")
}
#[deprecated(note = "use ArgValue::arg_value")]
pub fn arg_literal_value(&self, name: &str) -> Result<Lit, Error> {
self.args
.get(name)
.ok_or(Error::ArgRequired {
attr: self.name.clone(),
arg: name.to_owned(),
})?
.to_literal_value()
}
pub fn has_verbatim(&self, verbatim: &str) -> bool {
self.paths.iter().any(|path| path.is_ident(verbatim))
}
pub fn verbatim(&self) -> HashSet<String> {
self.paths
.iter()
.filter_map(Path::get_ident)
.map(|ident| ident.to_string())
.collect()
}
pub fn merge(&mut self, other: Self) -> Result<(), Error> {
if self.name != other.name {
return Err(Error::NamesDontMatch(self.name.clone(), other.name));
}
self.args.extend(other.args);
self.paths.extend(other.paths);
self.integers.extend(other.integers);
self.floats.extend(other.floats);
self.chars.extend(other.chars);
match (&mut self.string, &other.string) {
(_, None) => {}
(None, Some(_)) => self.string = other.string.clone(),
(Some(str1), Some(str2)) => {
*str1 = LitStr::new(&format!("{} {}", str1.value(), str2.value()), str1.span());
}
}
match (&mut self.bytes, &other.bytes) {
(_, None) => {}
(None, Some(_)) => self.bytes = other.bytes.clone(),
(Some(bytes1), Some(bytes2)) => {
let mut joined = bytes1.value();
joined.extend(bytes2.value());
*bytes1 = LitByteStr::new(&joined, bytes1.span());
}
}
match (&mut self.bool, &other.bool) {
(_, None) => {}
(None, Some(_)) => self.bool = other.bool.clone(),
(Some(_), Some(_)) => return Err(Error::MultipleLiteralValues(self.name.clone())),
}
Ok(())
}
#[inline]
pub fn merged(mut self, other: Self) -> Result<Self, Error> {
self.merge(other)?;
Ok(self)
}
#[inline]
pub fn enrich(&mut self, attr: &Attribute) -> Result<(), Error> {
self.merge(ParametrizedAttr::from_attribute(attr)?)
}
#[inline]
pub fn enriched(mut self, attr: &Attribute) -> Result<Self, Error> {
self.enrich(attr)?;
Ok(self)
}
#[inline]
pub fn fuse(&mut self, attr: &Attribute) -> Result<(), Error> {
let args = MetaArgList::parse.parse2(attr.tokens.clone().into())?;
for arg in args.list {
match arg {
MetaArg::Literal(Lit::Str(s)) => {
let span = s.span();
match self.string {
None => self.string = Some(s),
Some(ref mut str1) => {
let mut joined = str1.value();
joined.push_str(&s.value());
*str1 = LitStr::new(&joined, span);
}
}
}
MetaArg::Literal(Lit::ByteStr(s)) => {
let span = s.span();
match self.bytes {
None => self.bytes = Some(s),
Some(ref mut str1) => {
let mut joined = str1.value();
joined.extend(&s.value());
*str1 = LitByteStr::new(&joined, span);
}
}
}
MetaArg::Literal(Lit::Int(lit)) => self.integers.push(lit),
MetaArg::Literal(Lit::Float(lit)) => self.floats.push(lit),
MetaArg::Literal(Lit::Char(lit)) => self.chars.push(lit),
MetaArg::Literal(Lit::Bool(_)) if self.bool.is_some() => {
return Err(Error::MultipleLiteralValues(self.name.clone()));
}
MetaArg::Literal(Lit::Bool(ref lit)) if self.bool.is_none() => {
self.bool = Some(lit.clone())
}
MetaArg::Literal(_) => return Err(Error::UnsupportedLiteral(self.name.clone())),
MetaArg::Path(path) => self.paths.push(path),
MetaArg::NameValue(MetaArgNameValue { name, value, .. }) => {
let id = name.to_string();
if self.args.insert(id.clone(), value).is_some() {
return Err(Error::ArgNameMustBeUnique {
attr: self.name.clone(),
arg: id,
});
}
}
}
}
Ok(())
}
#[inline]
pub fn fused(mut self, attr: &Attribute) -> Result<Self, Error> {
self.fuse(attr)?;
Ok(self)
}
pub fn check(&mut self, req: AttrReq) -> Result<(), Error> {
for (name, req) in &req.arg_req {
if let Some(pos) = self.paths.iter().position(|path| path.is_ident(name)) {
self.paths.remove(pos);
self.args
.entry(name.clone())
.or_insert_with(|| req.default_value());
}
if !self.args.contains_key(name) && req.is_required() {
return Err(Error::ArgRequired {
attr: self.name.clone(),
arg: name.clone(),
});
}
}
for (name, value) in &mut self.args {
let req = if let Some(req) = req.arg_req.get(name) {
req
} else {
return Err(Error::AttributeUnknownArgument {
attr: self.name.clone(),
arg: name.clone(),
});
};
req.check(value, &self.name, name)?;
}
req.path_req.check(&mut self.paths, &self.name, "path")?;
req.integer_req
.check(&mut self.integers, &self.name, "integer literal")?;
req.float_req
.check(&mut self.floats, &self.name, "float literal")?;
req.char_req
.check(&mut self.chars, &self.name, "char literal")?;
req.string_req
.check(&mut self.string, &self.name, "string literal")?;
req.bool_req
.check(&mut self.bool, &self.name, "bool literal")?;
req.bytes_req
.check(&mut self.bytes, &self.name, "byte string literal")?;
Ok(())
}
#[inline]
pub fn checked(mut self, req: AttrReq) -> Result<Self, Error> {
self.check(req)?;
Ok(self)
}
}
#[doc(hidden)]
pub trait ExtractAttr {
#[doc(hidden)]
fn singular_attr(
self,
name: impl ToString + AsRef<str>,
req: ArgValueReq,
) -> Result<Option<SingularAttr>, Error>;
#[doc(hidden)]
fn parametrized_attr(
self,
name: impl ToString + AsRef<str>,
req: AttrReq,
) -> Result<Option<ParametrizedAttr>, Error>;
}
impl<'a, T> ExtractAttr for T
where T: IntoIterator<Item = &'a Attribute>
{
fn singular_attr(
self,
name: impl ToString + AsRef<str>,
req: ArgValueReq,
) -> Result<Option<SingularAttr>, Error> {
let mut attr = SingularAttr::new(name.to_string());
let filtered = self
.into_iter()
.filter(|attr| attr.path.is_ident(&name))
.collect::<Vec<_>>();
if filtered.is_empty() {
return Ok(None);
}
for entries in filtered {
attr.enrich(entries)?;
}
Some(attr.checked(req)).transpose()
}
fn parametrized_attr(
self,
name: impl ToString + AsRef<str>,
req: AttrReq,
) -> Result<Option<ParametrizedAttr>, Error> {
let mut attr = ParametrizedAttr::new(name.to_string());
let filtered = self
.into_iter()
.filter(|attr| attr.path.is_ident(&name))
.collect::<Vec<_>>();
if filtered.is_empty() {
return Ok(None);
}
for entries in filtered {
attr.enrich(entries)?;
}
Some(attr.checked(req)).transpose()
}
}
impl Debug for ParametrizedAttr {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str("ParametrizedAttr({")?;
if f.alternate() {
f.write_str("\n\t")?;
}
write!(f, "name: {:?}, ", self.name)?;
if f.alternate() {
f.write_str("\n\t")?;
}
f.write_str("args: {")?;
if !self.args.is_empty() {
if f.alternate() {
f.write_str("\n")?;
}
for (name, val) in &self.args {
if f.alternate() {
f.write_str("\t\t")?;
}
write!(f, "{} => {:?}, ", name, val)?;
if f.alternate() {
f.write_str("\n")?;
}
}
if f.alternate() {
f.write_str("\t")?;
}
}
f.write_str("}, ")?;
if f.alternate() {
f.write_str("\n\t")?;
}
f.write_str("paths: [")?;
if !self.paths.is_empty() {
if f.alternate() {
f.write_str("\n")?;
}
for path in &self.paths {
if f.alternate() {
f.write_str("\t\t")?;
}
write!(f, "{}, ", quote! { #path })?;
if f.alternate() {
f.write_str("\n")?;
}
}
if f.alternate() {
f.write_str("\t")?;
}
}
f.write_str("], ")?;
if f.alternate() {
f.write_str("\n\t")?;
}
write!(
f,
"bool: {:?}, ",
self.bool
.as_ref()
.map(|b| format!("Some({:?})", b.value))
.unwrap_or_else(|| "None".to_owned())
)?;
if f.alternate() {
f.write_str("\n\t")?;
}
write!(
f,
"string: {:?}, ",
self.string
.as_ref()
.map(|s| format!("Some({:?})", s.value()))
.unwrap_or_else(|| "None".to_owned())
)?;
if f.alternate() {
f.write_str("\n\t")?;
}
write!(
f,
"bytes: {:?}, ",
self.bytes
.as_ref()
.map(|s| format!("Some({:?})", s.value()))
.unwrap_or_else(|| "None".to_owned())
)?;
if f.alternate() {
f.write_str("\n\t")?;
}
f.write_str("chars: [")?;
if !self.chars.is_empty() {
if f.alternate() {
f.write_str("\n")?;
}
for c in &self.chars {
if f.alternate() {
f.write_str("\t\t")?;
}
write!(f, "{}, ", quote! { #c })?;
if f.alternate() {
f.write_str("\n")?;
}
}
if f.alternate() {
f.write_str("\t")?;
}
}
f.write_str("], ")?;
if f.alternate() {
f.write_str("\n\t")?;
}
f.write_str("integers: [")?;
if !self.integers.is_empty() {
if f.alternate() {
f.write_str("\n")?;
}
for c in &self.integers {
if f.alternate() {
f.write_str("\t\t")?;
}
write!(f, "{}, ", quote! { #c })?;
if f.alternate() {
f.write_str("\n")?;
}
}
if f.alternate() {
f.write_str("\t")?;
}
}
f.write_str("], ")?;
if f.alternate() {
f.write_str("\n\t")?;
}
f.write_str("floats: [")?;
if !self.floats.is_empty() {
if f.alternate() {
f.write_str("\n")?;
}
for c in &self.floats {
if f.alternate() {
f.write_str("\t\t")?;
}
write!(f, "{}, ", quote! { #c })?;
if f.alternate() {
f.write_str("\n")?;
}
}
if f.alternate() {
f.write_str("\t")?;
}
}
f.write_str("], ")?;
if f.alternate() {
f.write_str("\n")?;
}
f.write_str("})")?;
if f.alternate() {
f.write_str("\n")?;
}
Ok(())
}
}