mod import_index;
mod import_path;
mod re_exports;
pub use import_index::ImportIndex;
pub(crate) use import_index::{EntryVisibility, ImportIndexEntry};
pub use import_path::{EagerImportPath2Id, ImportPath2Id, LazyImportPath2Id};
pub(crate) use re_exports::ExternalReExport;
pub use re_exports::ExternalReExports;
use guppy::PackageId;
use indexmap::IndexSet;
use rustdoc_types::{ItemEnum, Visibility};
use crate::crate_data::CrateData;
use crate::queries::Crate;
pub trait CrateIndexer: Send + Sync {
type Annotations: Default + Send + Sync + serde::Serialize + serde::de::DeserializeOwned;
fn index_raw(
&self,
krate: rustdoc_types::Crate,
package_id: PackageId,
) -> IndexResult<Self::Annotations>;
fn index(&self, crate_data: CrateData, package_id: PackageId)
-> IndexResult<Self::Annotations>;
}
pub struct IndexResult<A> {
pub krate: Crate,
pub annotations: A,
pub can_cache_indexes: bool,
}
pub struct NoAnnotations;
impl CrateIndexer for NoAnnotations {
type Annotations = ();
fn index_raw(&self, krate: rustdoc_types::Crate, package_id: PackageId) -> IndexResult<()> {
use crate::crate_data::{
CrateItemIndex, CrateItemPaths, EagerCrateItemIndex, EagerCrateItemPaths,
};
let crate_data = CrateData {
root_item_id: krate.root,
index: CrateItemIndex::Eager(EagerCrateItemIndex { index: krate.index }),
external_crates: krate.external_crates,
format_version: krate.format_version,
paths: CrateItemPaths::Eager(EagerCrateItemPaths { paths: krate.paths }),
};
self.index(crate_data, package_id)
}
fn index(&self, crate_data: CrateData, package_id: PackageId) -> IndexResult<()> {
let krate = Crate::index_without_visitor(crate_data, package_id);
IndexResult {
krate,
annotations: (),
can_cache_indexes: true,
}
}
}
pub trait IndexingVisitor {
fn on_item_discovered(&mut self, item: &rustdoc_types::Item, item_id: rustdoc_types::Id);
fn on_type_indexed(&mut self, item_id: rustdoc_types::Id);
}
pub(crate) struct NoopVisitor;
impl IndexingVisitor for NoopVisitor {
fn on_item_discovered(&mut self, _item: &rustdoc_types::Item, _item_id: rustdoc_types::Id) {}
fn on_type_indexed(&mut self, _item_id: rustdoc_types::Id) {}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn index_local_types<'a, V: IndexingVisitor>(
krate: &'a CrateData,
package_id: &'a PackageId,
mut navigation_history: IndexSet<rustdoc_types::Id>,
mut current_path: Vec<String>,
import_index: &mut ImportIndex,
re_exports: &mut ExternalReExports,
visitor: &mut V,
current_item_id: &rustdoc_types::Id,
is_public: bool,
renamed_to: Option<String>,
encountered_use: bool,
) {
let current_item = match krate.index.get(current_item_id) {
None => {
if let Some(summary) = krate.paths.get(current_item_id)
&& summary.kind == rustdoc_types::ItemKind::Primitive
{
return;
}
panic!(
"Failed to retrieve item id `{:?}` from the JSON `index` for package id `{}`.",
¤t_item_id,
package_id.repr()
)
}
Some(i) => i,
};
visitor.on_item_discovered(current_item.as_ref(), *current_item_id);
let is_public = is_public && current_item.visibility == Visibility::Public;
let mut add_to_import_index = |path: Vec<String>, is_module: bool| {
let visibility = if is_public {
EntryVisibility::Public
} else {
EntryVisibility::Private
};
let is_definition = !encountered_use;
let index = if is_module {
&mut import_index.modules
} else {
&mut import_index.items
};
match index.get_mut(current_item_id) {
Some(entry) => {
entry.insert(path.clone(), visibility);
if is_definition {
entry.defined_at = Some(path);
}
}
None => {
index.insert(
*current_item_id,
ImportIndexEntry::new(path, visibility, is_definition),
);
}
}
};
let current_item = current_item.as_ref();
match ¤t_item.inner {
ItemEnum::Module(m) => {
let current_path_segment = renamed_to.unwrap_or_else(|| {
current_item
.name
.as_deref()
.expect("All 'module' items have a 'name' property")
.to_owned()
});
current_path.push(current_path_segment);
add_to_import_index(
current_path
.iter()
.map(|s| s.to_string())
.collect::<Vec<_>>(),
true,
);
navigation_history.insert(*current_item_id);
for item_id in &m.items {
index_local_types(
krate,
package_id,
navigation_history.clone(),
current_path.clone(),
import_index,
re_exports,
visitor,
item_id,
is_public,
None,
encountered_use,
);
}
}
ItemEnum::Use(i) => {
let Some(imported_id) = &i.id else {
return;
};
import_index
.re_export2parent_module
.insert(current_item.id, *navigation_history.last().unwrap());
let Some(imported_item) = krate.index.get(imported_id) else {
re_exports.insert(krate, current_item, ¤t_path);
return;
};
if let ItemEnum::Module(re_exported_module) = &imported_item.inner {
if !i.is_glob {
current_path.push(i.name.clone());
}
let infinite_loop = !navigation_history.insert(*imported_id);
if !infinite_loop {
for re_exported_item_id in &re_exported_module.items {
index_local_types(
krate,
package_id,
navigation_history.clone(),
current_path.clone(),
import_index,
re_exports,
visitor,
re_exported_item_id,
is_public,
None,
true,
);
}
}
} else {
navigation_history.insert(*imported_id);
if matches!(
imported_item.inner,
ItemEnum::Enum(_)
| ItemEnum::Struct(_)
| ItemEnum::Trait(_)
| ItemEnum::Function(_)
| ItemEnum::Primitive(_)
| ItemEnum::TypeAlias(_)
| ItemEnum::Static(_)
| ItemEnum::Union(_)
) {
let mut normalized_source_path = vec![];
let source_segments = i.source.split("::");
for segment in source_segments {
if segment == "self" {
normalized_source_path
.extend(current_path.iter().map(|s| s.to_string()));
} else if segment == "crate" {
normalized_source_path.push(current_path[0].to_string())
} else {
normalized_source_path.push(segment.to_string());
}
}
match import_index.items.get_mut(imported_id) {
Some(entry) => {
entry.insert_private(normalized_source_path);
}
None => {
import_index.items.insert(
*imported_id,
ImportIndexEntry::new(
normalized_source_path,
EntryVisibility::Private,
false,
),
);
}
}
}
index_local_types(
krate,
package_id,
navigation_history,
current_path.clone(),
import_index,
re_exports,
visitor,
imported_id,
is_public,
Some(i.name.clone()),
true,
);
}
}
ItemEnum::Trait(_)
| ItemEnum::Primitive(_)
| ItemEnum::Function(_)
| ItemEnum::Enum(_)
| ItemEnum::Struct(_)
| ItemEnum::TypeAlias(_)
| ItemEnum::Static(_)
| ItemEnum::Union(_)
| ItemEnum::Constant { .. } => {
let name = current_item.name.as_deref().expect(
"All 'struct', 'function', 'enum', 'type_alias', 'primitive', 'trait', 'static', 'union' and 'constant' items have a 'name' property",
);
if matches!(current_item.inner, ItemEnum::Primitive(_)) {
current_path.push("primitive".into());
}
current_path.push(renamed_to.unwrap_or_else(|| name.to_owned()));
let path: Vec<_> = current_path.into_iter().map(|s| s.to_string()).collect();
add_to_import_index(path, false);
visitor.on_type_indexed(*current_item_id);
}
_ => {}
}
}