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(),
}
}
fn item_vis_and_desc(item: &syn::Item) -> Option<(&syn::Visibility, String)> {
match item {
syn::Item::Fn(i) => Some((&i.vis, format!("fn {}", i.sig.ident))),
syn::Item::Struct(i) => Some((&i.vis, format!("struct {}", i.ident))),
syn::Item::Enum(i) => Some((&i.vis, format!("enum {}", i.ident))),
syn::Item::Union(i) => Some((&i.vis, format!("union {}", i.ident))),
syn::Item::Type(i) => Some((&i.vis, format!("type {}", i.ident))),
syn::Item::Const(i) => Some((&i.vis, format!("const {}", i.ident))),
syn::Item::Static(i) => Some((&i.vis, format!("static {}", i.ident))),
syn::Item::Trait(i) => Some((&i.vis, format!("trait {}", i.ident))),
syn::Item::TraitAlias(i) => Some((&i.vis, format!("trait {} (alias)", i.ident))),
syn::Item::Mod(i) => Some((&i.vis, format!("mod {}", i.ident))),
syn::Item::ExternCrate(i) => Some((&i.vis, format!("extern crate {}", i.ident))),
syn::Item::Use(i) => Some((
&i.vis,
format!(
"use {}{}",
if i.leading_colon.is_some() { "::" } else { "" },
use_tree_desc(&i.tree)
),
)),
_ => None,
}
}
pub(crate) fn item_finding(item: &syn::Item, ceiling_rank: u8) -> Option<String> {
let (vis, desc) = item_vis_and_desc(item)?;
(visibility_rank(vis) > ceiling_rank).then(|| format!("{} {}", vis_prefix(vis), desc))
}
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(", "))
}
}
}