evitable-derive-core 0.5.0

Derive implementation for evitable
Documentation
use super::*;
use std::borrow::Borrow;

struct Attr {
  meta: Meta,
  name: String,
  used: bool,
}

pub(crate) struct Attrs {
  attrs: Vec<Attr>,
}

impl Attrs {
  pub fn from_attributes<A, I>(iter: I) -> Result<Self>
  where
    A: Borrow<Attribute>,
    I: IntoIterator<Item = A>,
  {
    let mut vec = Vec::new();
    for item in iter {
      let attr = item.borrow();
      if let Ok(meta) = attr.meta() {
        if meta.is("evitable") {
          if let Meta::List(items) = meta {
            for item in items {
              match item {
                NestedMeta::Literal(l) => return Err(Error::unexpected_lit_type(&l).with_span(&l)),
                NestedMeta::Meta(m) => match m.path().single_ident() {
                  None => return Err(Error::unexpected_type("path").with_span(m.path())),
                  Some(ident) => {
                    let name = ident.to_string();
                    vec.push(Attr {
                      meta: m,
                      name,
                      used: false,
                    })
                  }
                },
              }
            }
          } else {
            return Err(Error::unexpected_type("Expected meta list.").with_span(attr));
          }
        }
      }
    }

    Ok(Attrs { attrs: vec })
  }

  pub fn get_optional<T: FromMeta, S: AsRef<str>>(&mut self, name: S) -> Result<Option<T>> {
    let name = name.as_ref();
    for attr in self.attrs.iter_mut() {
      if &attr.name == name {
        let result = FromMeta::from_meta(&attr.meta);
        attr.used = true;
        return result.map(Some);
      }
    }

    Ok(None)
  }

  pub fn get_required<T: FromMeta, S: AsRef<str>, O: Spanned>(
    &mut self,
    name: S,
    span: &O,
  ) -> Result<T> {
    self
      .get_optional(&name)
      .and_then(|v| v.ok_or_else(|| Error::missing_field(name.as_ref()).with_span(span)))
  }

  pub fn get_list<T: FromMeta, S: AsRef<str>>(&mut self, name: S) -> Result<Vec<T>> {
    let name = name.as_ref();
    let mut ret = Vec::new();
    for attr in self.attrs.iter_mut() {
      if &attr.name == name {
        let items = <Vec<T> as FromMeta>::from_meta(&attr.meta)?;
        attr.used = true;
        ret.extend(items);
      }
    }

    Ok(ret)
  }

  pub fn ensure_used(&self) -> Result<()> {
    for attr in self.attrs.iter() {
      if !attr.used {
        return Err(Error::unknown_field(&attr.name).with_span(attr.meta.path()));
      }
    }

    Ok(())
  }
}