use crate::resolve::strip_raw;
pub(crate) fn has_path_attr(attrs: &[syn::Attribute]) -> bool {
attrs.iter().any(is_path_remap)
}
fn is_path_remap(attr: &syn::Attribute) -> bool {
if attr.path().is_ident("path") {
return true;
}
if attr.path().is_ident("cfg_attr") {
if let Ok(metas) = attr.parse_args_with(cfg_attr_metas) {
return applied_metas_remap(&metas);
}
}
false
}
type MetaList = syn::punctuated::Punctuated<syn::Meta, syn::Token![,]>;
fn cfg_attr_metas(input: syn::parse::ParseStream) -> syn::Result<MetaList> {
MetaList::parse_terminated(input)
}
fn applied_metas_remap(metas: &MetaList) -> bool {
metas.iter().skip(1).any(meta_is_path_remap)
}
fn meta_is_path_remap(meta: &syn::Meta) -> bool {
match meta {
syn::Meta::NameValue(nv) => nv.path.is_ident("path"),
syn::Meta::List(list) if list.path.is_ident("cfg_attr") => list
.parse_args_with(cfg_attr_metas)
.map(|metas| applied_metas_remap(&metas))
.unwrap_or(false),
_ => false,
}
}
pub(crate) fn is_public(vis: &syn::Visibility) -> bool {
matches!(vis, syn::Visibility::Public(_))
}
pub(crate) fn visibility_rank(vis: &syn::Visibility) -> u8 {
match vis {
syn::Visibility::Public(_) => 3,
syn::Visibility::Restricted(r) => {
let single = if r.path.leading_colon.is_none() && r.path.segments.len() == 1 {
r.path.segments.first().map(|s| s.ident.to_string())
} else {
None
};
match single.as_deref() {
Some("crate") => 2,
Some("super") => 1,
Some("self") => 0,
_ => 2,
}
}
syn::Visibility::Inherited => 0,
}
}
fn vis_prefix(vis: &syn::Visibility) -> String {
match vis {
syn::Visibility::Public(_) => "pub".to_string(),
syn::Visibility::Restricted(r) => {
let path: Vec<String> = r
.path
.segments
.iter()
.map(|s| strip_raw(&s.ident.to_string()))
.collect();
let joined = path.join("::");
if r.in_token.is_some() && !matches!(joined.as_str(), "crate" | "super" | "self") {
format!("pub(in {joined})")
} else {
format!("pub({joined})")
}
}
syn::Visibility::Inherited => String::new(),
}
}
pub(crate) struct VisibleItem<'a> {
pub(crate) visibility: &'a syn::Visibility,
pub(crate) kind: &'static str,
pub(crate) name: String,
}
fn item_observation_parts(item: &syn::Item) -> Option<VisibleItem<'_>> {
let observed = |visibility, kind, name| VisibleItem {
visibility,
kind,
name,
};
match item {
syn::Item::Fn(i) => Some(observed(&i.vis, "fn", i.sig.ident.to_string())),
syn::Item::Struct(i) => Some(observed(&i.vis, "struct", i.ident.to_string())),
syn::Item::Enum(i) => Some(observed(&i.vis, "enum", i.ident.to_string())),
syn::Item::Union(i) => Some(observed(&i.vis, "union", i.ident.to_string())),
syn::Item::Type(i) => Some(observed(&i.vis, "type", i.ident.to_string())),
syn::Item::Const(i) => Some(observed(&i.vis, "const", i.ident.to_string())),
syn::Item::Static(i) => Some(observed(&i.vis, "static", i.ident.to_string())),
syn::Item::Trait(i) => Some(observed(&i.vis, "trait", i.ident.to_string())),
syn::Item::TraitAlias(i) => Some(observed(&i.vis, "trait_alias", i.ident.to_string())),
syn::Item::Mod(i) => Some(observed(&i.vis, "mod", i.ident.to_string())),
syn::Item::ExternCrate(i) => Some(observed(&i.vis, "extern_crate", i.ident.to_string())),
syn::Item::Use(i) => Some(observed(
&i.vis,
"use",
format!(
"{}{}",
if i.leading_colon.is_some() { "::" } else { "" },
use_tree_desc(&i.tree)
),
)),
_ => None,
}
}
pub(crate) fn item_observation(
item: &syn::Item,
ceiling_rank: u8,
) -> Option<(String, &'static str, String)> {
let observed = item_observation_parts(item)?;
(visibility_rank(observed.visibility) > ceiling_rank).then(|| {
(
vis_prefix(observed.visibility),
observed.kind,
observed.name,
)
})
}
fn use_tree_desc(tree: &syn::UseTree) -> String {
match tree {
syn::UseTree::Path(p) => {
format!(
"{}::{}",
strip_raw(&p.ident.to_string()),
use_tree_desc(&p.tree)
)
}
syn::UseTree::Name(n) => strip_raw(&n.ident.to_string()),
syn::UseTree::Rename(r) => format!(
"{} as {}",
strip_raw(&r.ident.to_string()),
strip_raw(&r.rename.to_string())
),
syn::UseTree::Glob(_) => "*".to_string(),
syn::UseTree::Group(g) => {
let inner: Vec<String> = g.items.iter().map(use_tree_desc).collect();
format!("{{{}}}", inner.join(", "))
}
}
}