1use fieldwork::Fieldwork;
2use rustdoc_types::{Crate, ExternalCrate, Id, Item, ItemKind};
3use semver::{Version, VersionReq};
4use std::collections::HashMap;
5use std::fmt::{self, Debug, Formatter};
6use std::ops::Deref;
7use std::path::PathBuf;
8
9use crate::CrateProvenance;
10use crate::doc_ref::{self, DocRef};
11use crate::navigator::{Navigator, parse_docsrs_url};
12
13#[derive(Clone, Fieldwork, PartialEq, Eq)]
15#[fieldwork(get, rename_predicates)]
16pub struct RustdocData {
17 pub(crate) crate_data: Crate,
18 pub(crate) name: String,
19 pub(crate) provenance: CrateProvenance,
20 pub(crate) fs_path: PathBuf,
21 pub(crate) version: Option<Version>,
22
23 #[field = false]
35 pub(crate) path_to_id: HashMap<String, Id>,
36}
37
38impl Debug for RustdocData {
39 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
40 f.debug_struct("RustdocData")
41 .field("name", &self.name)
42 .field("crate_type", &self.provenance)
43 .field("fs_path", &self.fs_path)
44 .field("version", &self.version)
45 .finish()
46 }
47}
48
49impl Deref for RustdocData {
50 type Target = Crate;
51
52 fn deref(&self) -> &Self::Target {
53 &self.crate_data
54 }
55}
56
57impl RustdocData {
58 pub(crate) fn get<'a>(&'a self, navigator: &'a Navigator, id: &Id) -> Option<DocRef<'a, Item>> {
59 let item = self.crate_data.index.get(id)?;
60 Some(DocRef::new(navigator, self, item))
61 }
62
63 pub fn path<'a>(&'a self, id: &Id) -> Option<doc_ref::Path<'a>> {
64 self.paths.get(id).map(|summary| summary.into())
65 }
66
67 pub fn root_item<'a>(&'a self, navigator: &'a Navigator) -> DocRef<'a, Item> {
68 DocRef::new(navigator, self, &self.index[&self.root])
69 }
70
71 pub fn traverse_to_crate_by_id<'a>(
72 &'a self,
73 navigator: &'a Navigator,
74 id: u32,
75 ) -> Option<&'a RustdocData> {
76 if id == 0 {
77 return Some(self);
79 }
80
81 let ExternalCrate {
82 name,
83 html_root_url,
84 ..
85 } = self.external_crates.get(&id)?;
86
87 let (name, version_req) = html_root_url.as_deref().and_then(parse_docsrs_url).map_or(
88 (&**name, VersionReq::STAR),
89 |(name, version)| {
90 let version_req =
91 VersionReq::parse(&format!("={version}")).unwrap_or(VersionReq::STAR);
92
93 (name, version_req)
94 },
95 );
96
97 navigator.load_crate(name, &version_req)
98 }
99
100 pub(crate) fn get_path<'a>(
101 &'a self,
102 navigator: &'a Navigator,
103 id: Id,
104 ) -> Option<DocRef<'a, Item>> {
105 let item_summary = self.paths.get(&id)?;
106 let crate_ = self.traverse_to_crate_by_id(navigator, item_summary.crate_id)?;
107 crate_
108 .root_item(navigator)
109 .find_by_path(item_summary.path.iter().skip(1))
110 }
111
112 pub(crate) fn build_path_index(&mut self) {
120 let mut by_unqualified: HashMap<String, Vec<(Id, ItemKind)>> = HashMap::new();
122 for (id, summary) in &self.crate_data.paths {
123 if summary.crate_id != 0 {
124 continue;
125 }
126 let Some(tail) = summary.path.get(1..) else {
127 continue;
128 };
129 if tail.is_empty() {
130 continue;
131 }
132 by_unqualified
133 .entry(tail.join("::"))
134 .or_default()
135 .push((*id, summary.kind));
136 }
137
138 let mut map = HashMap::new();
139 for (unqualified, items) in &by_unqualified {
140 let (prefix, last_name) = match unqualified.rfind("::") {
143 Some(sep) => (&unqualified[..sep + 2], &unqualified[sep + 2..]),
144 None => ("", unqualified.as_str()),
145 };
146
147 for (id, kind) in items {
149 let qualified = format!("{prefix}{}@{last_name}", kind_discriminator(*kind));
150 map.insert(qualified, *id);
151 }
152
153 if items.len() == 1 {
155 map.insert(unqualified.clone(), items[0].0);
156 }
157 }
158
159 self.path_to_id = map;
160 }
161}
162
163pub(crate) fn kind_discriminator(kind: ItemKind) -> &'static str {
170 match kind {
171 ItemKind::Module => "mod",
172 ItemKind::Struct => "struct",
173 ItemKind::Enum => "enum",
174 ItemKind::Union => "union",
175 ItemKind::Trait => "trait",
176 ItemKind::TraitAlias => "traitalias",
177 ItemKind::Function => "fn",
178 ItemKind::TypeAlias => "tyalias",
179 ItemKind::AssocType => "type",
180 ItemKind::Constant | ItemKind::AssocConst => "const",
181 ItemKind::Static => "static",
182 ItemKind::Macro => "macro",
183 ItemKind::ProcAttribute => "attr",
184 ItemKind::ProcDerive => "derive",
185 ItemKind::Primitive => "prim",
186 ItemKind::Variant => "variant",
187 ItemKind::StructField => "field",
188 ItemKind::Keyword => "keyword",
189 ItemKind::Attribute => "attribute",
190 ItemKind::ExternCrate | ItemKind::Use | ItemKind::Impl | ItemKind::ExternType => "item",
191 }
192}