1#![allow(non_snake_case)]
2use super::{DModule, DocTree, Show};
3use crate::type_name::{long, long_path};
4use crate::util::{xformat, CompactStringExt, XString};
5use rustdoc_types::{Crate, Id, Item, ItemEnum, ItemKind, ItemSummary};
6use std::{borrow::Borrow, cell::RefCell, collections::HashMap};
7
8mod impls;
10
11pub type IDs = Box<[ID]>;
12
13#[derive(
14 Clone, Default, Hash, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
15)]
16#[repr(transparent)]
17pub struct ID {
18 pub id: XString,
19}
20
21pub trait IdToID: Sized {
22 fn to_ID(&self) -> ID;
23 fn into_ID(self) -> ID;
24}
25
26impl IdToID for Id {
27 fn to_ID(&self) -> ID {
28 self.0.to_ID()
29 }
30
31 fn into_ID(self) -> ID {
32 String::into_ID(self.0)
33 }
34}
35
36impl IdToID for String {
37 fn to_ID(&self) -> ID {
38 ID {
39 id: self.as_str().into(),
40 }
41 }
42
43 fn into_ID(self) -> ID {
44 ID {
45 id: XString::from_string_buffer(self),
46 }
47 }
48}
49
50impl IdToID for &str {
51 fn to_ID(&self) -> ID {
52 ID::new(self)
53 }
54
55 fn into_ID(self) -> ID {
56 ID::new(self)
57 }
58}
59
60pub trait SliceToIds {
61 fn to_ids(&self) -> IDs;
62}
63impl<T: IdToID> SliceToIds for [T] {
64 fn to_ids(&self) -> IDs {
65 self.iter().map(|id| id.to_ID()).collect()
66 }
67}
68
69pub trait IdAsStr {
70 fn id_str(&self) -> &str;
71}
72impl IdAsStr for str {
73 fn id_str(&self) -> &str {
74 self
75 }
76}
77impl IdAsStr for ID {
78 fn id_str(&self) -> &str {
79 self
80 }
81}
82impl IdAsStr for Id {
83 fn id_str(&self) -> &str {
84 &self.0
85 }
86}
87impl<T: IdAsStr> IdAsStr for &T {
88 fn id_str(&self) -> &str {
89 T::id_str(self)
90 }
91}
92
93#[derive(serde::Serialize, serde::Deserialize)]
96pub struct IDMap {
97 krate: Crate,
98 dmod: DModule,
99 id_buffer: RefCell<String>,
100}
101
102impl IDMap {
103 pub fn new(krate: Crate) -> IDMap {
104 let mut map = IDMap {
105 krate,
106 dmod: DModule::default(),
108 id_buffer: RefCell::new(String::with_capacity(24)),
109 };
110 map.dmod = DModule::new(&map);
111 info!("IDMap and DModule ready");
112 map
113 }
114
115 pub fn dmodule(&self) -> &DModule {
116 &self.dmod
117 }
118
119 pub fn raw_crate_doc(&self) -> &Crate {
120 &self.krate
121 }
122}
123
124impl Default for IDMap {
125 fn default() -> Self {
126 let (crate_version, includes_private, index, paths, external_crates, format_version) =
127 Default::default();
128 IDMap {
129 krate: Crate {
130 root: rustdoc_types::Id(String::new()),
131 crate_version,
132 includes_private,
133 index,
134 paths,
135 external_crates,
136 format_version,
137 },
138 dmod: DModule::default(),
139 id_buffer: RefCell::default(),
140 }
141 }
142}
143
144pub type IndexMap = HashMap<Id, Item>;
146pub type PathMap = HashMap<Id, ItemSummary>;
148
149impl IDMap {
150 pub fn use_id<T>(&self, id: &str, f: impl FnOnce(&Id) -> T) -> T {
152 let mut buf = self.id_buffer.take();
154 buf.clear();
155 buf.push_str(id);
156
157 let id = Id(buf);
158 let val = f(&id);
159
160 self.id_buffer.replace(id.0);
162
163 val
164 }
165
166 pub fn indexmap(&self) -> &IndexMap {
167 &self.krate.index
168 }
169
170 pub fn pathmap(&self) -> &PathMap {
171 &self.krate.paths
172 }
173}
174
175impl IDMap {
177 pub fn dmodule_show_prettier(&self) -> DocTree {
179 self.dmod.show_prettier(self)
180 }
181
182 pub fn dmodule_item_tree(&self) -> DocTree {
184 self.dmod.item_tree(self)
185 }
186}
187
188impl IDMap {
190 pub fn get_doc(&self, id: &str) -> Option<&str> {
191 self.get_item(id).and_then(|item| match &item.inner {
192 ItemEnum::Import(item) => {
193 if let Some(inner_id) = item.id.as_ref() {
194 if let Some(reexport_item) = self.get_item(&inner_id.0) {
195 if matches!(reexport_item.inner, ItemEnum::Import(_)) {
196 error!(
197 "Reexport item with Id({id}) shouldn't \
198 recursively contains another Import.\n{item:?} "
199 );
200 } else {
201 return reexport_item.docs.as_deref();
202 }
203 }
204 }
205 None
206 }
207 _ => item.docs.as_deref(),
208 })
209 }
210}
211
212impl IDMap {
214 pub fn get_item(&self, id: &str) -> Option<&Item> {
215 self.use_id(id, |id| self.indexmap().get(id))
216 }
217
218 pub fn name<S>(&self, id: &S) -> XString
241 where
242 S: ?Sized + IdAsStr,
243 {
244 let id = id.id_str();
245 if let Some(item) = self.get_item(id) {
246 let name = item.name.as_deref().map(XString::from);
247 name.unwrap_or_else(|| item_name(item).unwrap_or_else(|| id.into()))
248 } else if let Some(path) = self.get_path(id) {
249 path.path.last().map(|p| p.as_str()).unwrap_or(id).into()
250 } else {
251 id.into()
252 }
253 }
254}
255
256fn item_name(item: &Item) -> Option<XString> {
258 match &item.inner {
259 ItemEnum::Impl(item) => {
260 let implementor = item
261 .blanket_impl
262 .as_ref()
263 .map_or_else(|| long(&item.for_), long);
264 if let Some(trait_) = item.trait_.as_ref().map(long_path) {
265 Some(xformat!("{implementor}: {trait_}"))
266 } else {
267 Some(xformat!("{implementor}"))
268 }
269 }
270 ItemEnum::Import(item) => Some(item.name.as_str().into()),
271 _ => None,
272 }
273}
274
275impl IDMap {
277 pub fn get_path(&self, id: &str) -> Option<&ItemSummary> {
278 self.use_id(id, |id| self.pathmap().get(id))
279 }
280
281 fn use_path_well(&self, id: &str, f: impl FnOnce(&ItemSummary) -> XString) -> XString {
287 match self.get_path(id).map(f) {
288 Some(s) => s,
289 None => {
290 warn!("Id({id}) doesn't refer to an Item in IndexMap");
291 XString::from(id)
292 }
293 }
294 }
295
296 pub fn path_with_kind_check<S, K>(&self, id: &S, kind: K) -> XString
299 where
300 S: ?Sized + IdAsStr,
301 K: Borrow<ItemKind>,
302 {
303 let id = id.id_str();
304 self.use_path_well(id, move |item| {
305 let kind = kind.borrow();
306 if &item.kind != kind {
307 warn!(
308 "Id({id}) in PathMap is found as {:?}, but {kind:?} is required",
309 item.kind
310 );
311 }
312 item.path.join_compact("::")
313 })
314 }
315
316 pub fn path(&self, id: &str) -> XString {
318 self.get_path(id)
319 .map(|item| item.path.join_compact("::"))
320 .unwrap_or_else(|| self.name(id))
321 }
322
323 pub fn path_or_name(&self, id: &str) -> Result<XString, XString> {
325 self.get_path(id)
326 .map(|item| item.path.join_compact("::"))
327 .ok_or_else(|| self.name(id))
328 }
329}