use crate::DocRef;
use crate::iterators::{LazyChild, LazyChildren};
use rustdoc_types::{Item, ItemEnum};
use std::cell::RefCell;
use std::collections::HashSet;
pub(crate) enum Step<T> {
Continue,
Stop(T),
}
pub(crate) fn find_named<'a>(parent: DocRef<'a, Item>, target: &str) -> Option<DocRef<'a, Item>> {
for_each_named(parent, target, Step::Stop)
}
pub(crate) fn for_each_named<'a, T>(
parent: DocRef<'a, Item>,
target: &str,
mut visit: impl FnMut(DocRef<'a, Item>) -> Step<T>,
) -> Option<T> {
visit_named(parent, target, &mut visit)
}
fn visit_named<'a, T>(
parent: DocRef<'a, Item>,
target: &str,
visit: &mut dyn FnMut(DocRef<'a, Item>) -> Step<T>,
) -> Option<T> {
if matches!(
parent.inner(),
ItemEnum::Struct(_) | ItemEnum::Enum(_) | ItemEnum::Union(_) | ItemEnum::Trait(_)
) {
for method in parent.methods() {
if method.name() == Some(target)
&& let Step::Stop(v) = visit(method)
{
return Some(v);
}
}
}
let mut globs: Vec<LazyChild<'a>> = Vec::new();
for child in LazyChildren::new(parent) {
match child {
LazyChild::Item(item) => {
if item.name() == Some(target)
&& let Step::Stop(v) = visit(item)
{
return Some(v);
}
}
LazyChild::NonGlob { use_item, .. } => {
if use_item.item().name == target
&& let Some(resolved) = child.resolve()
&& let Step::Stop(v) = visit(resolved)
{
return Some(v);
}
}
LazyChild::Glob { .. } => {
globs.push(child);
}
}
}
let _guard = GlobGuard::enter(parent.item(), target)?;
for glob in globs {
let Some(source_module) = glob.resolve() else {
continue;
};
if let Some(v) = visit_named(source_module, target, visit) {
return Some(v);
}
}
None
}
pub(crate) struct GlobGuard {
key: (usize, Vec<u8>),
}
thread_local! {
static IN_FLIGHT: RefCell<HashSet<(usize, Vec<u8>)>> = RefCell::new(HashSet::new());
}
impl GlobGuard {
pub(crate) fn enter(item: &Item, target: &str) -> Option<Self> {
let key = (item as *const Item as usize, target.as_bytes().to_vec());
let inserted = IN_FLIGHT.with(|s| s.borrow_mut().insert(key.clone()));
inserted.then_some(Self { key })
}
}
impl Drop for GlobGuard {
fn drop(&mut self) {
IN_FLIGHT.with(|s| {
s.borrow_mut().remove(&self.key);
});
}
}