use proc_macro2::Span;
use quote::quote;
use syn::{punctuated::Pair, Attribute, Ident, Meta, MetaList, NestedMeta, Path};
pub fn nested_meta_to_path(nested_meta: &NestedMeta) -> Option<&Path> {
match nested_meta {
NestedMeta::Meta(meta) => Some(meta.path()),
NestedMeta::Lit(..) => None, }
}
pub fn meta_list_contains(meta_list: &MetaList, operand: &NestedMeta) -> bool {
meta_list
.nested
.iter()
.any(|nested_meta| nested_meta == operand)
}
pub fn ident_concat(left: &str, right: &str) -> Ident {
let mut combined = String::with_capacity(left.len() + right.len());
combined.push_str(left);
combined.push_str(right);
Ident::new(&combined, Span::call_site())
}
pub fn contains_tag(attrs: &[Attribute], namespace: &Path, tag: &Path) -> bool {
attrs
.iter()
.map(Attribute::parse_meta)
.filter_map(Result::ok)
.filter(|meta| meta.path() == namespace)
.any(|meta| {
if let Meta::List(meta_list) = meta {
meta_list
.nested
.iter()
.filter_map(|nested_meta| {
if let NestedMeta::Meta(meta) = nested_meta {
Some(meta)
} else {
None }
})
.any(|meta| meta.path() == tag)
} else {
false
}
})
}
#[allow(clippy::let_and_return)] pub fn namespace_parameter(attrs: &[Attribute], namespace: &Path) -> Option<NestedMeta> {
let error_message = {
format!(
"Expected exactly one identifier for `#[{}(..)]`.",
format_path(namespace),
)
};
let namespace_meta_lists_iter = namespace_meta_lists_iter(attrs, namespace);
let meta_param = namespace_meta_lists_iter
.map(|meta_list| {
if meta_list.nested.len() != 1 {
panic!("{}. `{:?}`", &error_message, &meta_list.nested);
}
meta_list
.nested
.into_pairs()
.map(Pair::into_value)
.next()
.expect("Expected one meta item to exist.")
})
.next();
meta_param
}
pub fn namespace_parameters(attrs: &[Attribute], namespace: &Path) -> Vec<NestedMeta> {
let namespace_meta_lists_iter = namespace_meta_lists_iter(attrs, namespace);
let parameters = namespace_meta_lists_iter
.flat_map(|meta_list| meta_list.nested.into_pairs().map(Pair::into_value))
.collect::<Vec<NestedMeta>>();
parameters
}
#[allow(clippy::let_and_return)] pub fn tag_parameter(attrs: &[Attribute], namespace: &Path, tag: &Path) -> Option<NestedMeta> {
let error_message = {
format!(
"Expected exactly one identifier for `#[{}({}(..))]`.",
format_path(namespace),
format_path(tag),
)
};
let namespace_meta_lists_iter = namespace_meta_lists_iter(attrs, namespace);
let meta_param = tag_meta_lists_owned_iter(namespace_meta_lists_iter, tag)
.map(|meta_list| {
if meta_list.nested.len() != 1 {
panic!("{}. `{:?}`", &error_message, &meta_list.nested);
}
meta_list
.nested
.into_pairs()
.map(Pair::into_value)
.next()
.expect("Expected one meta item to exist.")
})
.next();
meta_param
}
pub fn tag_parameters(attrs: &[Attribute], namespace: &Path, tag: &Path) -> Vec<NestedMeta> {
let namespace_meta_lists_iter = namespace_meta_lists_iter(attrs, namespace);
let parameters = tag_meta_lists_owned_iter(namespace_meta_lists_iter, tag)
.flat_map(|meta_list| meta_list.nested.into_pairs().map(Pair::into_value))
.collect::<Vec<NestedMeta>>();
parameters
}
pub fn namespace_meta_lists_iter<'f>(
attrs: &'f [Attribute],
namespace: &'f Path,
) -> impl Iterator<Item = MetaList> + 'f {
attrs
.iter()
.map(Attribute::parse_meta)
.filter_map(Result::ok)
.filter(move |meta| meta.path() == namespace)
.filter_map(|meta| {
if let Meta::List(meta_list) = meta {
Some(meta_list)
} else {
None
}
})
}
pub fn namespace_meta_lists(attrs: &[Attribute], namespace: &Path) -> Vec<MetaList> {
namespace_meta_lists_iter(attrs, namespace).collect::<Vec<MetaList>>()
}
pub fn tag_meta_lists_iter<'f>(
namespace_meta_lists_iter: &'f [MetaList],
tag: &'f Path,
) -> impl Iterator<Item = &'f MetaList> + 'f {
namespace_meta_lists_iter
.iter()
.flat_map(|meta_list| meta_list.nested.iter())
.filter_map(|nested_meta| {
if let NestedMeta::Meta(meta) = nested_meta {
Some(meta)
} else {
None }
})
.filter(move |meta| meta.path() == tag)
.filter_map(|meta| {
if let Meta::List(meta_list) = meta {
Some(meta_list)
} else {
None }
})
}
pub fn tag_meta_lists_owned_iter<'f>(
namespace_meta_lists_iter: impl Iterator<Item = MetaList> + 'f,
tag: &'f Path,
) -> impl Iterator<Item = MetaList> + 'f {
namespace_meta_lists_iter
.flat_map(|meta_list| meta_list.nested.into_pairs().map(Pair::into_value))
.filter_map(|nested_meta| {
if let NestedMeta::Meta(meta) = nested_meta {
Some(meta)
} else {
None }
})
.filter(move |meta| meta.path() == tag)
.filter_map(|meta| {
if let Meta::List(meta_list) = meta {
Some(meta_list)
} else {
None }
})
}
pub fn format_path(path: &Path) -> String {
quote!(#path)
.to_string()
.chars()
.filter(|c| !c.is_whitespace())
.collect::<String>()
}