use std::hash::Hash;
use std::fmt::{Debug};
use std::collections::HashMap;
use syn::{Type, Path, Attribute, Meta, MetaList, MetaNameValue, NestedMeta, Lit, LitInt, LitStr};
use proc_macro2::{Ident, Span};
use crate::Error;
use syn::spanned::Spanned;
#[derive(Clone)]
pub enum Attr {
Singular(SingularAttr),
Parametrized(ParametrizedAttr),
}
#[derive(Clone)]
pub struct SingularAttr {
pub name: Ident,
pub value: Option<ArgValue>,
}
#[derive(Clone)]
pub struct ParametrizedAttr {
pub name: Ident,
pub args: HashMap<String, ArgValue>,
pub paths: Vec<Path>,
pub integers: Vec<LitInt>,
pub literal: Option<Lit>,
}
#[derive(Clone)]
pub enum ArgValue {
Lit(Lit),
Type(Type),
}
#[derive(Clone)]
pub struct AttrReq {
pub args: HashMap<String, ValueReq<ArgValue>>,
pub paths: ListReq<Path>,
pub integers: ListReq<LitInt>,
pub literal: (LitReq, ValueReq<Lit>),
}
#[derive(Clone)]
pub enum ValueReq<T>
where
T: Clone,
{
Required,
Default(T),
Optional,
Prohibited,
}
#[derive(Clone)]
pub enum ListReq<T>
where
T: Clone,
{
NoneOrMore,
OneOrMore,
Default(T),
Deny,
}
#[derive(Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
pub enum LitReq {
StringLiteral,
ByteLiteral,
CharLiteral,
IntLiteral,
FloatLiteral,
BoolLiteral,
Verbatim,
}
impl Attr {
#[inline]
pub fn singular_or_err(self) -> Result<SingularAttr, Error> {
match self {
Attr::Singular(attr) => Ok(attr),
Attr::Parametrized(attr) => Err(Error::SingularAttrRequired(attr.name)),
}
}
#[inline]
pub fn parametrized_or_err(self) -> Result<ParametrizedAttr, Error> {
match self {
Attr::Singular(attr) => Err(Error::ParametrizedAttrRequired(attr.name)),
Attr::Parametrized(attr) => Ok(attr),
}
}
#[inline]
pub fn name(&self) -> Ident {
match self {
Attr::Singular(attr) => attr.name.clone(),
Attr::Parametrized(attr) => attr.name.clone(),
}
}
#[inline]
pub fn value(&self) -> Result<ArgValue, Error> {
match self {
Attr::Singular(attr) => attr.value(),
Attr::Parametrized(attr) => Err(Error::ParametrizedAttrHasNoValue(attr.name.clone())),
}
}
#[inline]
pub fn literal_value(&self) -> Result<Lit, Error> {
self.value()?.literal_value()
}
#[inline]
pub fn type_value(&self) -> Result<Type, Error> {
self.value()?.type_value()
}
pub fn with_attribute(attr: &Attribute) -> Result<Self, Error> {
SingularAttr::with_attribute(attr)
.map(|singular| Attr::Singular(singular))
.or_else(|_| {
ParametrizedAttr::with_attribute(attr).map(|param| Attr::Parametrized(param))
})
}
}
impl SingularAttr {
#[inline]
pub fn with_name(name: Ident) -> Self {
Self { name, value: None }
}
#[inline]
pub fn with_named_literal(name: Ident, lit: Lit) -> Self {
Self {
name,
value: Some(ArgValue::Lit(lit)),
}
}
pub fn with_attribute(attr: &Attribute) -> Result<Self, Error> {
let ident = attr
.path
.get_ident()
.cloned()
.ok_or(Error::ArgNameMustBeIdent)?;
match attr.parse_meta()? {
Meta::Path(_) => unreachable!(),
Meta::NameValue(MetaNameValue { lit, .. }) => {
Ok(SingularAttr::with_named_literal(ident, lit))
}
Meta::List(_) => Err(Error::SingularAttrRequired(ident)),
}
}
#[inline]
pub fn value(&self) -> Result<ArgValue, Error> {
self.value
.as_ref()
.cloned()
.ok_or(Error::ArgValueRequired(self.name.clone()))
}
#[inline]
pub fn literal_value(&self) -> Result<Lit, Error> {
self.value()?.literal_value()
}
#[inline]
pub fn type_value(&self) -> Result<Type, Error> {
self.value()?.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.clone()));
}
match (&self.value, &other.value) {
(_, None) => {}
(None, Some(_)) => self.value = other.value,
(Some(_), Some(_)) => 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::with_attribute(attr)?)
}
#[inline]
pub fn enriched(mut self, attr: &Attribute) -> Result<Self, Error> {
self.enrich(attr)?;
Ok(self)
}
pub fn check<T>(&self, _req: ValueReq<T>) -> Result<(), Error>
where
T: Clone + Eq + PartialEq + Hash + Debug,
{
Ok(())
}
#[inline]
pub fn checked<T>(self, req: ValueReq<T>) -> Result<Self, Error>
where
T: Clone + Eq + PartialEq + Hash + Debug,
{
self.check(req)?;
Ok(self)
}
}
impl ParametrizedAttr {
#[inline]
pub fn with_name(name: Ident) -> Self {
Self {
name,
args: Default::default(),
paths: vec![],
integers: vec![],
literal: None,
}
}
pub fn with_attribute(attr: &Attribute) -> Result<Self, Error> {
let ident = attr
.path
.get_ident()
.cloned()
.ok_or(Error::ArgNameMustBeIdent)?;
match attr.parse_meta()? {
Meta::List(MetaList { nested, .. }) => nested
.into_iter()
.fold(Ok(ParametrizedAttr::with_name(ident)), |res, nested| {
res.and_then(|attr| attr.fused(nested))
}),
_ => Err(Error::ParametrizedAttrRequired(ident)),
}
}
pub fn arg_literal_value(&self, name: &str) -> Result<Lit, Error> {
self.args
.get(name)
.ok_or(Error::NamedArgRequired(name.to_owned()))?
.literal_value()
}
pub fn has_verbatim(&self, verbatim: &str) -> bool {
self.paths
.iter()
.find(|path| path.is_ident(verbatim))
.is_some()
}
pub fn merge(&mut self, other: Self) -> Result<(), Error> {
if self.name != other.name {
return Err(Error::NamesDontMatch(self.name.clone(), other.name.clone()));
}
self.args.extend(other.args);
self.paths.extend(other.paths);
self.integers.extend(other.integers);
let span = self.literal.span();
match (&mut self.literal, &other.literal) {
(_, None) => {}
(None, Some(_)) => self.literal = other.literal,
(Some(Lit::Str(str1)), Some(Lit::Str(str2))) => {
let mut joined = str1.value();
joined.push_str(&str2.value());
*str1 = LitStr::new(&joined, span);
}
(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::with_attribute(attr)?)
}
#[inline]
pub fn enriched(mut self, attr: &Attribute) -> Result<Self, Error> {
self.enrich(attr)?;
Ok(self)
}
#[inline]
pub fn fuse(&mut self, nested: NestedMeta) -> Result<(), Error> {
match nested {
NestedMeta::Lit(Lit::Str(str2)) => {
let span = self.literal.span();
match self.literal {
None => self.literal = Some(Lit::Str(str2)),
Some(Lit::Str(ref mut str1)) => {
let mut joined = str1.value();
joined.push_str(&str2.value());
*str1 = LitStr::new(&joined, span);
}
Some(_) => return Err(Error::MultipleLiteralValues(self.name.clone())),
}
}
NestedMeta::Lit(Lit::Int(litint)) => self.integers.push(litint),
NestedMeta::Lit(lit) => self
.literal
.as_mut()
.map(|l| *l = lit)
.ok_or(Error::MultipleLiteralValues(self.name.clone()))?,
NestedMeta::Meta(Meta::Path(path)) => self.paths.push(path),
NestedMeta::Meta(Meta::NameValue(MetaNameValue { path, lit, .. })) => {
let id = path
.clone()
.get_ident()
.cloned()
.ok_or(Error::ArgNameMustBeIdent)?;
if self
.args
.insert(id.to_string(), ArgValue::Lit(lit))
.is_some()
{
return Err(Error::ArgNameMustBeUnique(id.clone()));
}
}
NestedMeta::Meta(Meta::List(_)) => {
return Err(Error::NestedListsNotSupported(self.name.clone()))
}
}
Ok(())
}
#[inline]
pub fn fused(mut self, nested: NestedMeta) -> Result<Self, Error> {
self.fuse(nested)?;
Ok(self)
}
pub fn check(&self, _req: AttrReq) -> Result<(), Error> {
Ok(())
}
#[inline]
pub fn checked(self, req: AttrReq) -> Result<Self, Error> {
self.check(req)?;
Ok(self)
}
}
impl ArgValue {
#[inline]
pub fn literal_value(&self) -> Result<Lit, Error> {
match self {
ArgValue::Lit(lit) => Ok(lit.clone()),
ArgValue::Type(_) => Err(Error::ArgValueMustBeLiteral),
}
}
#[inline]
pub fn type_value(&self) -> Result<Type, Error> {
match self {
ArgValue::Lit(_) => Err(Error::ArgValueMustBeType),
ArgValue::Type(ty) => Ok(ty.clone()),
}
}
}
#[doc(hide)]
pub trait ExtractAttr {
#[doc(hide)]
fn singular_attr<T>(
self,
name: &str,
) -> Result<Option<SingularAttr>, Error>;
#[doc(hide)]
fn parametrized_attr(
self,
name: &str,
) -> Result<Option<ParametrizedAttr>, Error>;
}
impl<'a, T> ExtractAttr for T
where
T: IntoIterator<Item = &'a Attribute>,
{
fn singular_attr<V>(
self,
name: &str,
) -> Result<Option<SingularAttr>, Error> {
let mut attr = SingularAttr::with_name(Ident::new(name, Span::call_site()));
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)?;
}
Ok(Some(attr))
}
fn parametrized_attr(
self,
name: &str,
) -> Result<Option<ParametrizedAttr>, Error> {
let mut attr = ParametrizedAttr::with_name(Ident::new(name, Span::call_site()));
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)?;
}
Ok(Some(attr))
}
}