// Rust language amplification derive library providing multiple generic trait
// implementations, type wrappers, derive macros and other language enhancements
//
// Written in 2019-2021 by
//     Dr. Maxim Orlovsky <orlovsky@pandoracore.com>
//
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to
// the public domain worldwide. This software is distributed without
// any warranty.
//
// You should have received a copy of the MIT License
// along with this software.
// If not, see <https://opensource.org/licenses/MIT>.

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};

/// Internal structure representation of a proc macro attribute collected
/// instances having some specific name (accessible via [`Attr::name()`]).
#[derive(Clone, Debug)]
pub enum Attr {
    /// Attribute of `#[attr]` or `#[attr = value]` form, which, aside from the
    /// case where `value` is a string literal, may have only a single
    /// occurrence (string literals are concatenated into a single value like
    /// rust compiler does for `#[doc = "..."]` attributes).
    Singular(SingularAttr),

    /// Parametrized attribute in form of `#[attr(...)]`, where parameters are
    /// gathered from all attribute occurrences.
    Parametrized(ParametrizedAttr),
}

/// Structure describing a procedural macro attribute with an optional value.
/// The means that if one has something like `#[name1]`, `#[name2 = "value"]`,
/// `#[name3 = ::std::path::PathBuf)]` than `name1`, `name2 = "value"`, and
/// `name3 = ::std::path::PathBuf` are three different attributes  which can be
/// parsed and represented by the [`SingularAttr`] structure.
///
/// NB: For `#[attr(arg1, arg2 = value)]` style of proc macros use
/// [`ParametrizedAttr`] structure. If you need to support both use [`Attr`]
/// enum.
///
/// Internally the structure is composed of the `name` and `value` fields,
/// where name is always a [`struct@syn::Ident`] (corresponding `name1`,
/// `name2`, `name3` from the sample above) and `value` is an optional literal
/// [`enum@Lit`], with corresponding cases of `None`,
/// `Some(`[`ArgValue::Literal`]`(`[`Lit::Str`]`(`[`struct@LitStr`]`)))`, and
/// `Some(`[`ArgValue::Type`]`(`[`Type::Path`]`(`[`Path`]`)))`.
#[derive(Clone, Debug)]
pub struct SingularAttr {
    /// Optional attribute argument path part; for instance in
    /// `#[my(name = value)]` or in `#[name = value]` this is a `name` part
    pub name: String,

    /// Attribute argument value part; for instance in `#[name = value]` this is
    /// the `value` part
    pub value: ArgValue,
}

/// Representation for all allowed forms of `#[attr(...)]` attribute.
/// If attribute has a multiple occurrences they are all assembled into a single
/// list. Repeated named arguments are not allowed and result in errors.
///
/// For situations like in `#[attr("string literal")]`, [`ParametrizedAttr`]
/// will have a `name` field set to `attr`, `literal` field set to
/// `Lit::LitStr(LitStr("string literal"))`, `args` will be an empty `HashSet`
/// and `paths` will be represented by an empty vector.
#[derive(Clone)]
pub struct ParametrizedAttr {
    /// Attribute name - `attr` part of `#[attr(...)]`
    pub name: String,

    /// All attribute arguments that have form of `#[attr(ident = "literal")]`
    /// or `#[attr(ident = TypeName)]` mapped to their name identifiers.
    pub args: HashMap<String, ArgValue>,

    /// All attribute arguments that are paths or identifiers without any
    /// specific value, like `#[attr(std::io::Error, crate, super::SomeType)]`.
    ///
    /// NB: All named arguments without the value assigned are getting into this
    /// field by default, even if they do not represent any known rust path or
    /// type name, like `#[attr(some_id, other)]`. However, with
    /// [`ParametrizedAttr::check`] and [`ParametrizedAttr::checked`] those
    /// values matching ones specified in [`AttrReq::arg_req`] with values set
    /// to [`crate::ListReq::Predefined::default`] are moved into
    /// [`ParametrizedAttr::args`].
    pub paths: Vec<Path>,

    /// Unnamed string literal found within attribute arguments.
    ///
    /// If multiple string literals are present they are concatenated into a
    /// single value, like it is done by the rust compiler for
    /// `#[doc = "..."]` attributes
    pub string: Option<LitStr>,

    /// Unnamed byte string literal found within attribute arguments.
    ///
    /// If multiple byte string literals are present they are concatenated into
    /// a single value, like it is done by the rust compiler for
    /// `#[doc = "..."]` attributes
    pub bytes: Option<LitByteStr>,

    /// Unnamed char literals found within attribute arguments
    pub chars: Vec<LitChar>,

    /// Unnamed integer literals found within attribute arguments
    pub integers: Vec<LitInt>,

    /// Unnamed float literals found within attribute arguments
    pub floats: Vec<LitFloat>,

    /// Unnamed bool literal found within attribute arguments.
    ///
    /// If multiple bool literals are present this will generate an error.
    pub bool: Option<LitBool>,
}

impl Attr {
    /// Constructs [`Attr`] from a vector of all syn-parsed attributes,
    /// selecting attributes matching the provided name.
    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))
    }

    /// Constructor parsing [`Attribute`] value and returning either
    /// [`SingularAttr`] or [`ParametrizedAttr`] packed in form of [`Attr`]
    /// enum.
    ///
    /// If the attribute does not match either of forms, a [`Error`] is
    /// returned. Currently, only single type of error may occur in practice:
    /// - [`Error::ArgNameMustBeIdent`], which happens if the attribute name is
    ///   not an [`struct@syn::Ident`] but is a complex path value
    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))
    }

    /// Returns inner value \in form of [`SingularAttr`] for [`Attr::Singular`]
    /// variant, or fails with [`Error::SingularAttrRequired`] otherwise
    #[inline]
    pub fn try_singular(self) -> Result<SingularAttr, Error> {
        match self {
            Attr::Singular(attr) => Ok(attr),
            Attr::Parametrized(attr) => Err(Error::SingularAttrRequired(attr.name)),
        }
    }

    /// Returns inner value \in form of [`ParametrizedAttr`] for
    /// [`Attr::Parametrized`] variant, or fails with
    /// [`Error::ParametrizedAttrRequired`] otherwise
    #[inline]
    pub fn try_parametrized(self) -> Result<ParametrizedAttr, Error> {
        match self {
            Attr::Singular(attr) => Err(Error::ParametrizedAttrRequired(attr.name)),
            Attr::Parametrized(attr) => Ok(attr),
        }
    }

    /// Returns string reference to the argument name
    #[inline]
    pub fn name(&self) -> &str {
        match self {
            Attr::Singular(attr) => &attr.name,
            Attr::Parametrized(attr) => &attr.name,
        }
    }

    /// Returns [`ArgValue`] for the [`Attr::Singular`] variant or fails with
    /// [`Error::ParametrizedAttrHasNoValue`]
    #[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())),
        }
    }

    /// Returns literal value for the [`Attr::Singular`] variant or fails with
    /// [`Error::ParametrizedAttrHasNoValue`]. See
    /// [`ArgValue::to_literal_value`] for more details.
    #[inline]
    pub fn literal_value(&self) -> Result<Lit, Error> { self.arg_value()?.to_literal_value() }

    /// Returns type value for the [`Attr::Singular`] variant or fails with
    /// [`Error::ParametrizedAttrHasNoValue`]. See
    /// [`ArgValue::to_literal_value`] for more details.
    #[inline]
    pub fn type_value(&self) -> Result<Type, Error> { self.arg_value()?.to_type_value() }
}

impl SingularAttr {
    /// Constructs named [`SingularAttr`] without value
    #[inline]
    pub fn new(name: impl ToString) -> Self {
        Self {
            name: name.to_string(),
            value: ArgValue::None,
        }
    }

    /// Constructs [`SingularAttr`] from a vector of all syn-parsed attributes,
    /// selecting single attribute matching the provided name. If there are
    /// multiple instances of the same attribute, fails with
    /// [`Error::SingularAttrRequired`]
    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
    }

    /// Constructs named [`SingularAttr`] setting its value to the provided
    /// literal
    #[inline]
    pub fn with_literal(name: impl ToString, lit: Lit) -> Self {
        Self {
            name: name.to_string(),
            value: ArgValue::Literal(lit),
        }
    }

    /// Constructs named [`SingularAttr`] setting its value to the provided
    /// rust type value
    #[inline]
    pub fn with_type(name: impl ToString, ty: Type) -> Self {
        Self {
            name: name.to_string(),
            value: ArgValue::Type(ty),
        }
    }

    /// Constructs [`SingularAttr`] from a given [`syn::Attribute`] by parsing
    /// its data. Accepts only attributes having form `#[attr(name = value)]`
    /// and errors for other attribute types with [`Error::ArgNameMustBeIdent`]
    /// and [`Error::SingularAttrRequired`]
    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()? {
            // `#[attr::path]` - unreachable: filtered in the code above
            Meta::Path(_) => unreachable!(),
            // `#[ident = lit]`
            Meta::NameValue(MetaNameValue { lit, .. }) => {
                Ok(SingularAttr::with_literal(ident, lit))
            }
            // `#[ident(...)]`
            Meta::List(_) => Err(Error::SingularAttrRequired(ident)),
        }
    }

    /// Returns literal value, if any, or fails with
    /// [`Error::ArgValueRequired`]. See [`ArgValue::to_literal_value`] for the
    /// details.
    #[inline]
    pub fn literal_value(&self) -> Result<Lit, Error> { self.value.to_literal_value() }

    /// Returns type value, if any, or fails with [`Error::ArgValueRequired`].
    /// See [`ArgValue::to_literal_value`] for the details.
    #[inline]
    pub fn type_value(&self) -> Result<Type, Error> { self.value.to_type_value() }

    /// Merges data from the `other` into the self.
    ///
    /// # Errors
    ///
    /// - Fails with [`Error::NamesDontMatch`] if the names of the self and the
    ///   `other` do not match
    /// - Fails with [`Error::MultipleSingularValues`] if both self and the
    ///   `other` has a named argument with the same name but different values.
    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(())
    }

    /// Does merging as in [`SingularAttr::merge`], but unlike it consumes
    /// the self and returns a merged structure in case of the successful
    /// operation. Useful in operation chains.
    #[inline]
    pub fn merged(mut self, other: Self) -> Result<Self, Error> {
        self.merge(other)?;
        Ok(self)
    }

    /// Enriches current attribute data by adding information from the provided
    /// [`syn::Attribute`].
    ///
    /// # Errors
    ///
    /// - Fails with [`Error::NamesDontMatch`] if the names of the self and the
    ///   provided attribute do not match
    /// - Fails with [`Error::MultipleSingularValues`] if both self and the
    ///   provided attribute has a named argument with the same name but
    ///   different values.
    #[inline]
    pub fn enrich(&mut self, attr: &Attribute) -> Result<(), Error> {
        self.merge(SingularAttr::from_attribute(attr)?)
    }

    /// Performs enrich operation as in [`SingularAttr::enrich`], but unlike it
    /// consumes the self and returns an enriched structure in case of the
    /// successful operation. Useful in operation chains.
    #[inline]
    pub fn enriched(mut self, attr: &Attribute) -> Result<Self, Error> {
        self.enrich(attr)?;
        Ok(self)
    }

    /// Checks that the structure meets provided value requirements (see
    /// [`ArgValueReq`]), generating [`Error`] if the requirements are not met.
    pub fn check(&mut self, req: ArgValueReq) -> Result<(), Error> {
        req.check(&mut self.value, &self.name, &self.name)?;
        Ok(())
    }

    /// Performs check as in [`SingularAttr::check`], but unlike it consumes the
    /// self and returns a itself in case of the successful operation.
    /// Useful in operation chains.
    #[inline]
    pub fn checked(mut self, req: ArgValueReq) -> Result<Self, Error> {
        self.check(req)?;
        Ok(self)
    }
}

impl ParametrizedAttr {
    /// Constructs named [`SingularAttr`] with empty internal data
    #[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,
        }
    }

    /// Constructs [`ParametrizedAttr`] from a vector of all syn-parsed
    /// attributes, selecting attributes matching the provided name.
    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)
    }

    /// Constructs [`ParametrizedAttr`] from a given [`syn::Attribute`]
    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)
    }

    /// Returns value for a given argument with name `name`, if it is defined,
    /// or fails with [`Error::ArgValueRequired`].
    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())
    }

    /// Returns value for a given argument with name `name`, if it is defined,
    /// or panics otherwise.
    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")
    }

    /// Returns literal value for a given argument with name `name`, if it is
    /// defined, or fails with [`Error::ArgValueRequired`]. See
    /// [`ArgValue::to_literal_value`] for the details.
    #[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()
    }

    /// Checks if the attribute has a verbatim argument matching the provided
    /// `verbatim` string.
    ///
    /// Verbatim arguments are arguments in form of `#[attr(verbatim1,
    /// verbatim2]`, i.e. path arguments containing single path segment and no
    /// value or nested arguments.
    pub fn has_verbatim(&self, verbatim: &str) -> bool {
        self.paths.iter().any(|path| path.is_ident(verbatim))
    }

    /// Returns set of verbatim attribute arguments.
    ///
    /// Verbatim arguments are arguments in form of `#[attr(verbatim1,
    /// verbatim2]`, i.e. path arguments containing single path segment and no
    /// value or nested arguments.
    pub fn verbatim(&self) -> HashSet<String> {
        self.paths
            .iter()
            .filter_map(Path::get_ident)
            .map(|ident| ident.to_string())
            .collect()
    }

    /// Merges data from the `other` into the self.
    ///
    /// # Errors
    ///
    /// - Fails with [`Error::NamesDontMatch`] if the names of the self and the
    ///   `other` do not match
    /// - Fails with [`Error::MultipleLiteralValues`] if both self and the
    ///   `other` has a literals which values are not equal.
    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(())
    }

    /// Does merging as in [`ParametrizedAttr::merge`], but unlike it consumes
    /// the self and returns a merged structure in case of the successful
    /// operation. Useful in operation chains.
    #[inline]
    pub fn merged(mut self, other: Self) -> Result<Self, Error> {
        self.merge(other)?;
        Ok(self)
    }

    /// Enriches current attribute data by adding information from the provided
    /// [`syn::Attribute`].
    ///
    /// # Errors
    ///
    /// - Fails with [`Error::NamesDontMatch`] if the names of the self and the
    ///   provided attribute do not match
    /// - Fails with [`Error::MultipleLiteralValues`] if both self and the
    ///   provided attribute has a literals which values are not equal.
    #[inline]
    pub fn enrich(&mut self, attr: &Attribute) -> Result<(), Error> {
        self.merge(ParametrizedAttr::from_attribute(attr)?)
    }

    /// Performs enrich operation as in [`ParametrizedAttr::enrich`], but unlike
    /// it consumes the self and returns an enriched structure in case of
    /// the successful operation. Useful in operation chains.
    #[inline]
    pub fn enriched(mut self, attr: &Attribute) -> Result<Self, Error> {
        self.enrich(attr)?;
        Ok(self)
    }

    /// Fuses data from a nested attribute arguments (see [`Attribute`]) into
    /// the attribute parameters.
    ///
    /// The operation is similar to the [`ParametrizedAttr::enrich`] with the
    /// only difference that enrichment operation takes the whole attribute, and
    /// fusion takes a nested meta data.
    #[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 {
                // `#[ident("literal", ...)]`
                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);
                        }
                    }
                }

                // `#[ident(b"literal", ...)]`
                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);
                        }
                    }
                }

                // `#[ident(3, ...)]`
                MetaArg::Literal(Lit::Int(lit)) => self.integers.push(lit),

                // `#[ident(2.3, ...)]`
                MetaArg::Literal(Lit::Float(lit)) => self.floats.push(lit),

                // `#[ident('a', ...)]`
                MetaArg::Literal(Lit::Char(lit)) => self.chars.push(lit),

                // `#[ident(true, ...)]`
                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())
                }

                // `#[ident(true, ...)]`
                MetaArg::Literal(_) => return Err(Error::UnsupportedLiteral(self.name.clone())),

                // `#[ident(arg::path)]`
                MetaArg::Path(path) => self.paths.push(path),

                // `#[ident(name = value, ...)]`
                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(())
    }

    /// Performs enrich operation as in [`ParametrizedAttr::fuse`], but unlike
    /// it consumes the self and returns an enriched structure in case of
    /// the successful operation. Useful in operation chains.
    #[inline]
    pub fn fused(mut self, attr: &Attribute) -> Result<Self, Error> {
        self.fuse(attr)?;
        Ok(self)
    }

    /// Checks that the structure meets provided value requirements (see
    /// [`AttrReq`]), generating [`Error`] if the requirements are not met.
    ///
    /// The procedure modifies the [`ParametrizedAttr`] data in the following
    /// ways:
    /// 1. First, it fills in [`ParametrizedAttr::paths`],
    ///    [`ParametrizedAttr::integers`], [`ParametrizedAttr::floats`],
    ///    [`ParametrizedAttr::chars`], [`ParametrizedAttr::bytes`],
    ///    [`ParametrizedAttr::string`] with default values from
    ///    [`AttrReq::path_req`], [`AttrReq::integer_req`],
    ///    [`AttrReq::float_req`], [`AttrReq::char_req`],
    ///    [`AttrReq::bytes_req`], [`AttrReq::string_req`] correspondingly.
    /// 2. [`ParametrizedAttr::paths`] values
    ///    matching ones specified in [`AttrReq::arg_req`] with values set to
    ///    [`crate::ListReq::Predefined::default`] are moved into
    ///    [`ParametrizedAttr::args`] field.
    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(())
    }

    /// Performs check as in [`ParametrizedAttr::check`], but unlike it
    /// consumes the self and returns a itself in case of the successful
    /// operation. Useful in operation chains.
    #[inline]
    pub fn checked(mut self, req: AttrReq) -> Result<Self, Error> {
        self.check(req)?;
        Ok(self)
    }
}

// This trait should not be implemented for the types outside of this crate
#[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>
{
    /// Returns a [`SingularAttr`] which structure must fulfill the provided
    /// requirements - or fails with a [`Error`] otherwise. For more information
    /// check [`ValueReq`] requirements info.
    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()
    }

    /// Returns a [`ParametrizedAttr`] which structure must fulfill the provided
    /// requirements - or fails with a [`Error`] otherwise. For more information
    /// check [`AttrReq`] requirements info.
    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(())
    }
}
