use crate::Navigator;
use crate::doc_ref::{DocRef, ParentRef};
use fieldwork::Fieldwork;
use rustdoc_types::{Id, Item, ItemEnum, Type, Use};
use std::collections::hash_map::Values;
fn resolve_use_source<'a>(
navigator: &'a Navigator,
module_path: &[&str],
source: &str,
) -> Option<DocRef<'a, Item>> {
let rewritten = rewrite_relative_prefix(module_path, source)?;
navigator.resolve_path(&rewritten, &mut vec![])
}
fn rewrite_relative_prefix(module_path: &[&str], source: &str) -> Option<String> {
let mut supers = 0;
let mut rest = source;
while let Some(tail) = rest.strip_prefix("super::") {
supers += 1;
rest = tail;
}
if supers > 0 || rest == "super" {
let supers = if rest == "super" { supers + 1 } else { supers };
let tail = if rest == "super" { "" } else { rest };
let remaining = module_path.len().checked_sub(supers)?;
if remaining == 0 {
return None;
}
let prefix = module_path[..remaining].join("::");
return Some(if tail.is_empty() {
prefix
} else {
format!("{prefix}::{tail}")
});
}
if let Some(tail) = source.strip_prefix("self::") {
return Some(format!("{}::{}", module_path.join("::"), tail));
}
if source == "self" {
return Some(module_path.join("::"));
}
if let Some(tail) = source.strip_prefix("crate::") {
let crate_name = module_path.first().copied()?;
return Some(format!("{crate_name}::{tail}"));
}
if source == "crate" {
return module_path.first().copied().map(String::from);
}
Some(source.to_owned())
}
pub struct MethodIter<'a> {
item: DocRef<'a, Item>,
impl_block_iter: InherentImplBlockIter<'a>,
current_item_iter: Option<std::slice::Iter<'a, Id>>,
}
impl<'a> MethodIter<'a> {
pub(crate) fn new(item: DocRef<'a, Item>) -> Self {
let impl_block_iter = InherentImplBlockIter::new(item);
Self {
item,
impl_block_iter,
current_item_iter: None,
}
}
}
impl<'a> DocRef<'a, Item> {
pub fn methods(&self) -> MethodIter<'a> {
MethodIter::new(*self)
}
pub fn traits(&self) -> TraitIter<'a> {
TraitIter::new(*self)
}
pub fn child_items(&self) -> ChildItems<'a> {
ChildItems::new(*self)
}
pub fn lazy_children(&self) -> LazyChildren<'a> {
LazyChildren::new(*self)
}
}
impl<'a> DocRef<'a, Item> {
pub fn id_iter(&self, ids: &'a [Id]) -> IdIter<'a> {
IdIter::new(*self, ids)
}
}
#[derive(Debug, Clone, Copy)]
pub enum LazyChild<'a> {
Item(DocRef<'a, Item>),
NonGlob {
use_item: DocRef<'a, Use>,
parent: DocRef<'a, Item>,
},
Glob {
use_item: DocRef<'a, Use>,
parent: DocRef<'a, Item>,
},
}
impl<'a> LazyChild<'a> {
pub fn name(&self) -> Option<&'a str> {
match self {
LazyChild::Item(item) => item.name(),
LazyChild::NonGlob { use_item, .. } => Some(&use_item.item().name),
LazyChild::Glob { .. } => None,
}
}
pub fn resolve(self) -> Option<DocRef<'a, Item>> {
match self {
LazyChild::Item(item) => Some(item),
LazyChild::NonGlob { use_item, parent } => {
let name: &'a str = &use_item.item().name;
resolve_use_target(use_item, parent).map(|item| item.with_name(name))
}
LazyChild::Glob { use_item, parent } => resolve_use_target(use_item, parent),
}
}
}
fn resolve_use_target<'a>(
use_item: DocRef<'a, Use>,
parent: DocRef<'a, Item>,
) -> Option<DocRef<'a, Item>> {
let use_inner = use_item.item();
use_inner
.id
.and_then(|id| use_item.crate_docs().get(use_item.navigator(), &id))
.or_else(|| {
let module_path = parent.containing_module_path();
resolve_use_source(use_item.navigator(), &module_path, &use_inner.source)
})
}
pub struct LazyChildren<'a> {
parent: DocRef<'a, Item>,
ids: std::slice::Iter<'a, Id>,
}
impl<'a> LazyChildren<'a> {
pub(crate) fn new(parent: DocRef<'a, Item>) -> Self {
let ids: &[Id] = match parent.inner() {
ItemEnum::Module(m) => &m.items,
ItemEnum::Enum(e) => &e.variants,
_ => &[],
};
Self {
parent,
ids: ids.iter(),
}
}
}
impl<'a> Iterator for LazyChildren<'a> {
type Item = LazyChild<'a>;
fn next(&mut self) -> Option<Self::Item> {
for id in &mut self.ids {
let Some(item) = self.parent.get(id) else {
continue;
};
return Some(match item.inner() {
ItemEnum::Use(use_item) => {
let use_ref = item.build_ref(use_item);
if use_item.is_glob {
LazyChild::Glob {
use_item: use_ref,
parent: self.parent,
}
} else {
LazyChild::NonGlob {
use_item: use_ref,
parent: self.parent,
}
}
}
_ => LazyChild::Item(item),
});
}
None
}
}
pub struct TraitIter<'a> {
item: DocRef<'a, Item>,
item_iter: Values<'a, Id, Item>,
}
impl<'a> TraitIter<'a> {
fn new(item: DocRef<'a, Item>) -> Self {
let item_iter = item.crate_docs().index.values();
Self { item, item_iter }
}
}
impl<'a> Iterator for TraitIter<'a> {
type Item = DocRef<'a, Item>;
fn next(&mut self) -> Option<Self::Item> {
for item in &mut self.item_iter {
if let ItemEnum::Impl(impl_block) = &item.inner
&& let Type::ResolvedPath(path) = &impl_block.for_
&& path.id == self.item.id
&& impl_block.trait_.is_some()
{
return Some(self.item.build_ref(item));
}
}
None
}
}
pub struct ImplementorIter<'a> {
trait_item: DocRef<'a, Item>,
item_iter: Values<'a, Id, Item>,
}
impl<'a> ImplementorIter<'a> {
fn new(trait_item: DocRef<'a, Item>) -> Self {
let item_iter = trait_item.crate_docs().index.values();
Self {
trait_item,
item_iter,
}
}
}
impl<'a> DocRef<'a, Item> {
pub fn implementors(&self) -> ImplementorIter<'a> {
ImplementorIter::new(*self)
}
}
impl<'a> Iterator for ImplementorIter<'a> {
type Item = DocRef<'a, Item>;
fn next(&mut self) -> Option<Self::Item> {
for item in &mut self.item_iter {
if let ItemEnum::Impl(impl_block) = &item.inner
&& let Some(trait_path) = &impl_block.trait_
&& trait_path.id == self.trait_item.id
&& !impl_block.is_negative
{
return Some(self.trait_item.build_ref(item));
}
}
None
}
}
impl<'a> Iterator for MethodIter<'a> {
type Item = DocRef<'a, Item>;
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some(current_item_iter) = &mut self.current_item_iter {
for id in current_item_iter {
if let Some(item) = self.item.get(id) {
return Some(item.with_parent(self.item));
}
}
}
if let Some(item) = self.impl_block_iter.next()
&& let ItemEnum::Impl(impl_block) = &item.item().inner
{
self.current_item_iter = Some(impl_block.items.iter())
} else {
return None;
}
}
}
}
#[derive(Debug, Fieldwork)]
pub struct IdIter<'a> {
item: DocRef<'a, Item>,
id_iter: std::slice::Iter<'a, Id>,
glob_iter: Option<Box<IdIter<'a>>>,
#[field(with)]
include_use: bool,
#[field(with(vis = "pub(crate)", option_set_some, into))]
parent: Option<ParentRef<'a>>,
}
impl<'a> IdIter<'a> {
pub(crate) fn new(item: DocRef<'a, Item>, ids: &'a [Id]) -> Self {
Self {
item,
id_iter: ids.iter(),
glob_iter: None,
include_use: false,
parent: None,
}
}
}
impl<'a> Iterator for IdIter<'a> {
type Item = DocRef<'a, Item>;
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some(glob_iter) = self.glob_iter.as_mut() {
if let Some(item) = glob_iter.next() {
return Some(item);
} else {
self.glob_iter = None;
}
}
for id in &mut self.id_iter {
if let Some(item) = self.item.get(id) {
if let ItemEnum::Use(use_item) = item.inner() {
if self.include_use {
return Some(item);
}
let Some(source_item) = use_item
.id
.and_then(|id| item.crate_docs().get(item.navigator(), &id))
.or_else(|| {
let module_path = self.item.containing_module_path();
resolve_use_source(item.navigator(), &module_path, &use_item.source)
})
else {
continue;
};
if use_item.is_glob {
self.glob_iter = match source_item.inner() {
ItemEnum::Module(module) => {
Some(Box::new(source_item.id_iter(&module.items)))
}
ItemEnum::Enum(enum_item) => {
Some(Box::new(source_item.id_iter(&enum_item.variants)))
}
_ => None,
};
break;
} else {
return Some(source_item.with_name(&use_item.name));
}
}
return Some(match self.parent {
Some(parent) => item.with_parent(parent),
None => item,
});
}
}
if self.glob_iter.is_none() {
break;
}
}
None
}
}
pub(crate) struct InherentImplBlockIter<'a> {
item: DocRef<'a, Item>,
item_iter: Values<'a, Id, Item>,
}
impl<'a> InherentImplBlockIter<'a> {
pub(crate) fn new(item: DocRef<'a, Item>) -> Self {
let item_iter = item.crate_docs().index.values();
Self { item, item_iter }
}
}
impl<'a> Iterator for InherentImplBlockIter<'a> {
type Item = DocRef<'a, Item>;
fn next(&mut self) -> Option<Self::Item> {
for item in &mut self.item_iter {
if let ItemEnum::Impl(impl_block) = &item.inner
&& let Type::ResolvedPath(path) = &impl_block.for_
&& path.id == self.item.id
&& impl_block.trait_.is_none()
{
return Some(DocRef::new(self.item.navigator(), self.item, item));
}
}
None
}
}
pub enum ChildItems<'a> {
AssociatedMethods(MethodIter<'a>),
Module(IdIter<'a>),
Use(Option<DocRef<'a, Use>>, Option<IdIter<'a>>, bool),
Enum(IdIter<'a>, MethodIter<'a>),
None,
}
impl<'a> Iterator for ChildItems<'a> {
type Item = DocRef<'a, Item>;
fn next(&mut self) -> Option<Self::Item> {
loop {
match self {
ChildItems::AssociatedMethods(method_iter) => return method_iter.next(),
ChildItems::Module(id_iter) => return id_iter.next(),
ChildItems::Enum(id_iter, method_iter) => {
return id_iter.next().or_else(|| method_iter.next());
}
ChildItems::Use(_, Some(id_iter), _) => return id_iter.next(),
ChildItems::Use(use_item_option @ Some(_), id_iter @ None, include_use) => {
let use_item = use_item_option.take()?;
let name = use_item.use_name();
let source_item = use_item
.id
.and_then(|id| use_item.get(&id))
.or_else(|| {
let crate_name = use_item.crate_docs().name();
resolve_use_source(
use_item.navigator(),
&[crate_name],
&use_item.source,
)
})?
.with_name(name);
if use_item.is_glob {
match source_item.inner() {
ItemEnum::Module(module) => {
*id_iter = Some(
source_item
.id_iter(&module.items)
.with_include_use(*include_use),
);
}
ItemEnum::Enum(enum_item) => {
*id_iter = Some(
source_item
.id_iter(&enum_item.variants)
.with_include_use(*include_use),
);
}
_ => {
return None;
}
}
} else if let ItemEnum::Use(ui) = source_item.inner()
&& !*include_use
{
*use_item_option = Some(source_item.build_ref(ui));
} else {
return Some(source_item);
}
}
ChildItems::Use(_, _, _) => return None,
ChildItems::None => return None,
}
}
}
}
impl<'a> ChildItems<'a> {
pub(crate) fn new(item: DocRef<'a, Item>) -> Self {
let parent = ParentRef::from(item);
match &item.item().inner {
ItemEnum::Module(module) => {
Self::Module(item.id_iter(&module.items).with_parent(parent))
}
ItemEnum::Enum(enum_item) => Self::Enum(
item.id_iter(&enum_item.variants).with_parent(parent),
item.methods(),
),
ItemEnum::Struct(_) => Self::AssociatedMethods(item.methods()),
ItemEnum::Use(use_item) => ChildItems::Use(Some(item.build_ref(use_item)), None, false),
_ => Self::None,
}
}
pub(crate) fn with_use(self) -> Self {
match self {
ChildItems::AssociatedMethods(method_iter) => {
ChildItems::AssociatedMethods(method_iter)
}
ChildItems::Module(id_iter) => ChildItems::Module(id_iter.with_include_use(true)),
ChildItems::Enum(id_iter, method_iter) => {
ChildItems::Enum(id_iter.with_include_use(true), method_iter)
}
ChildItems::Use(item, Some(id_iter), _) => {
ChildItems::Use(item, Some(id_iter.with_include_use(true)), true)
}
ChildItems::Use(item, None, _) => ChildItems::Use(item, None, true),
ChildItems::None => ChildItems::None,
}
}
}