use std::sync::Arc;
use quote::ToTokens;
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct RustCfgCondition {
predicates: Arc<[Arc<str>]>,
}
impl Default for RustCfgCondition {
fn default() -> Self {
Self {
predicates: Arc::from(Vec::<Arc<str>>::new()),
}
}
}
impl RustCfgCondition {
pub(crate) fn stated(predicate: &str) -> Self {
Self::normalized(vec![Arc::from(predicate)])
}
pub(crate) fn with(&self, attrs: &[syn::Attribute]) -> Self {
let stated = predicates_of(attrs);
match stated.is_empty() {
true => self.clone(),
false => Self::normalized(self.predicates.iter().cloned().chain(stated).collect()),
}
}
pub(crate) fn and(&self, other: &Self) -> Self {
match (self.is_empty(), other.is_empty()) {
(true, _) => other.clone(),
(_, true) => self.clone(),
_ => Self::normalized(
self.predicates
.iter()
.chain(other.predicates.iter())
.cloned()
.collect(),
),
}
}
pub(crate) fn is_empty(&self) -> bool {
self.predicates.is_empty()
}
fn normalized(mut predicates: Vec<Arc<str>>) -> Self {
predicates.sort();
predicates.dedup();
Self {
predicates: Arc::from(predicates),
}
}
}
pub(crate) fn cfg_list(attr: &syn::Attribute) -> Option<&syn::MetaList> {
match attr.path().is_ident("cfg") {
true => attr.meta.require_list().ok(),
false => None,
}
}
pub(crate) fn predicate_text(list: &syn::MetaList) -> Arc<str> {
Arc::from(list.tokens.to_string().as_str())
}
pub(crate) fn cfg_predicate(attr: &syn::Attribute) -> Option<Arc<str>> {
cfg_list(attr).map(predicate_text)
}
fn predicates_of(attrs: &[syn::Attribute]) -> Vec<Arc<str>> {
attrs
.iter()
.flat_map(|attr| match attr.path().is_ident("cfg_attr") {
true => conditional_predicates(attr),
false => cfg_predicate(attr).into_iter().collect(),
})
.collect()
}
fn conditional_predicates(attr: &syn::Attribute) -> Vec<Arc<str>> {
let Some((gate, applied)) = conditional_attribute(attr) else {
return Vec::new();
};
let rendered = gate.to_token_stream().to_string();
applied
.iter()
.filter_map(added_cfg)
.map(|added| Arc::from(format!("all({rendered}, {added})").as_str()))
.collect()
}
pub(crate) fn conditional_attribute(
attr: &syn::Attribute,
) -> Option<(syn::Meta, Box<[syn::Meta]>)> {
let metas = attr
.parse_args_with(syn::punctuated::Punctuated::<syn::Meta, syn::Token![,]>::parse_terminated)
.ok()?;
let mut stated = metas.into_iter();
let gate = stated.next()?;
Some((gate, stated.collect()))
}
fn added_cfg(meta: &syn::Meta) -> Option<String> {
let syn::Meta::List(list) = meta else {
return None;
};
list.path.is_ident("cfg").then(|| list.tokens.to_string())
}