1use std::borrow::Cow;
2use std::cmp::Ordering;
3use std::fmt;
4
5use askama::Template;
6use rustc_data_structures::fx::FxHashSet;
7use rustc_hir::def::CtorKind;
8use rustc_hir::def_id::{DefIdMap, DefIdSet};
9use rustc_middle::ty::{self, TyCtxt};
10use tracing::debug;
11
12use super::{Context, ItemSection, item_ty_to_section};
13use crate::clean;
14use crate::formats::Impl;
15use crate::formats::item_type::ItemType;
16use crate::html::format::{print_path, print_type};
17use crate::html::markdown::{IdMap, MarkdownWithToc};
18use crate::html::render::print_item::compare_names;
19
20#[derive(Clone, Copy)]
21pub(crate) enum ModuleLike {
22 Module,
23 Crate,
24}
25
26impl ModuleLike {
27 pub(crate) fn is_crate(self) -> bool {
28 matches!(self, ModuleLike::Crate)
29 }
30}
31impl<'a> From<&'a clean::Item> for ModuleLike {
32 fn from(it: &'a clean::Item) -> ModuleLike {
33 if it.is_crate() { ModuleLike::Crate } else { ModuleLike::Module }
34 }
35}
36
37#[derive(Template)]
38#[template(path = "sidebar.html")]
39pub(super) struct Sidebar<'a> {
40 pub(super) title_prefix: &'static str,
41 pub(super) title: &'a str,
42 pub(super) is_crate: bool,
43 pub(super) parent_is_crate: bool,
44 pub(super) is_mod: bool,
45 pub(super) blocks: Vec<LinkBlock<'a>>,
46 pub(super) path: String,
47}
48
49impl Sidebar<'_> {
50 pub fn should_render_blocks(&self) -> bool {
53 self.blocks.iter().any(LinkBlock::should_render)
54 }
55}
56
57pub(crate) struct LinkBlock<'a> {
59 heading: Link<'a>,
63 class: &'static str,
64 links: Vec<Link<'a>>,
65 force_render: bool,
67}
68
69impl<'a> LinkBlock<'a> {
70 pub fn new(heading: Link<'a>, class: &'static str, links: Vec<Link<'a>>) -> Self {
71 Self { heading, links, class, force_render: false }
72 }
73
74 pub fn forced(heading: Link<'a>, class: &'static str) -> Self {
75 Self { heading, links: vec![], class, force_render: true }
76 }
77
78 pub fn should_render(&self) -> bool {
79 self.force_render || !self.links.is_empty()
80 }
81}
82
83#[derive(PartialEq, Eq, Hash, Clone)]
85pub(crate) struct Link<'a> {
86 name: Cow<'a, str>,
88 name_html: Option<Cow<'a, str>>,
90 href: Cow<'a, str>,
92 children: Vec<Link<'a>>,
94}
95
96impl Ord for Link<'_> {
97 fn cmp(&self, other: &Self) -> Ordering {
98 match compare_names(&self.name, &other.name) {
99 Ordering::Equal => {}
100 result => return result,
101 }
102 (&self.name_html, &self.href, &self.children).cmp(&(
103 &other.name_html,
104 &other.href,
105 &other.children,
106 ))
107 }
108}
109
110impl PartialOrd for Link<'_> {
111 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
112 Some(self.cmp(other))
113 }
114}
115
116impl<'a> Link<'a> {
117 pub fn new(href: impl Into<Cow<'a, str>>, name: impl Into<Cow<'a, str>>) -> Self {
118 Self { href: href.into(), name: name.into(), children: vec![], name_html: None }
119 }
120 pub fn empty() -> Link<'static> {
121 Link::new("", "")
122 }
123}
124
125pub(crate) mod filters {
126 use std::fmt::{self, Display};
127
128 use askama::filters::Safe;
129
130 use crate::html::escape::EscapeBodyTextWithWbr;
131 pub(crate) fn wrapped<T, V: askama::Values>(v: T, _: V) -> askama::Result<Safe<impl Display>>
132 where
133 T: Display,
134 {
135 let string = v.to_string();
136 Ok(Safe(fmt::from_fn(move |f| EscapeBodyTextWithWbr(&string).fmt(f))))
137 }
138}
139
140pub(super) fn print_sidebar(
141 cx: &Context<'_>,
142 it: &clean::Item,
143 mut buffer: impl fmt::Write,
144) -> fmt::Result {
145 let mut ids = IdMap::new();
146 let mut blocks: Vec<LinkBlock<'_>> = docblock_toc(cx, it, &mut ids).into_iter().collect();
147 let deref_id_map = cx.deref_id_map.borrow();
148 match it.kind {
149 clean::StructItem(ref s) => sidebar_struct(cx, it, s, &mut blocks, &deref_id_map),
150 clean::TraitItem(ref t) => sidebar_trait(cx, it, t, &mut blocks, &deref_id_map),
151 clean::PrimitiveItem(_) => sidebar_primitive(cx, it, &mut blocks, &deref_id_map),
152 clean::UnionItem(ref u) => sidebar_union(cx, it, u, &mut blocks, &deref_id_map),
153 clean::EnumItem(ref e) => sidebar_enum(cx, it, e, &mut blocks, &deref_id_map),
154 clean::TypeAliasItem(ref t) => sidebar_type_alias(cx, it, t, &mut blocks, &deref_id_map),
155 clean::ModuleItem(ref m) => {
156 blocks.push(sidebar_module(&m.items, &mut ids, ModuleLike::from(it)))
157 }
158 clean::ForeignTypeItem => sidebar_foreign_type(cx, it, &mut blocks, &deref_id_map),
159 _ => {}
160 }
161 let (title_prefix, title) = if !blocks.is_empty() && !it.is_crate() {
171 (
172 match it.kind {
173 clean::ModuleItem(..) => "Module ",
174 _ => "",
175 },
176 it.name.as_ref().unwrap().as_str(),
177 )
178 } else {
179 ("", "")
180 };
181 let sidebar_path =
188 if it.is_mod() { &cx.current[..cx.current.len() - 1] } else { &cx.current[..] };
189 let path: String = if sidebar_path.len() > 1 || !title.is_empty() {
190 let path = sidebar_path.iter().map(|s| s.as_str()).intersperse("::").collect();
191 if sidebar_path.len() == 1 { format!("crate {path}") } else { path }
192 } else {
193 "".into()
194 };
195 let sidebar = Sidebar {
196 title_prefix,
197 title,
198 is_mod: it.is_mod(),
199 is_crate: it.is_crate(),
200 parent_is_crate: sidebar_path.len() == 1,
201 blocks,
202 path,
203 };
204 sidebar.render_into(&mut buffer)?;
205 Ok(())
206}
207
208fn get_struct_fields_name<'a>(fields: &'a [clean::Item]) -> Vec<Link<'a>> {
209 let mut fields = fields
210 .iter()
211 .filter(|f| matches!(f.kind, clean::StructFieldItem(..)))
212 .filter_map(|f| {
213 f.name.as_ref().map(|name| Link::new(format!("structfield.{name}"), name.as_str()))
214 })
215 .collect::<Vec<Link<'a>>>();
216 fields.sort();
217 fields
218}
219
220fn docblock_toc<'a>(
221 cx: &'a Context<'_>,
222 it: &'a clean::Item,
223 ids: &mut IdMap,
224) -> Option<LinkBlock<'a>> {
225 let (toc, _) = MarkdownWithToc {
226 content: &it.doc_value(),
227 links: &it.links(cx),
228 ids,
229 error_codes: cx.shared.codes,
230 edition: cx.shared.edition(),
231 playground: &cx.shared.playground,
232 }
233 .into_parts();
234 let links: Vec<Link<'_>> = toc
235 .entries
236 .into_iter()
237 .map(|entry| {
238 Link {
239 name_html: if entry.html == entry.name { None } else { Some(entry.html.into()) },
240 name: entry.name.into(),
241 href: entry.id.into(),
242 children: entry
243 .children
244 .entries
245 .into_iter()
246 .map(|entry| Link {
247 name_html: if entry.html == entry.name {
248 None
249 } else {
250 Some(entry.html.into())
251 },
252 name: entry.name.into(),
253 href: entry.id.into(),
254 children: vec![],
258 })
259 .collect(),
260 }
261 })
262 .collect();
263 if links.is_empty() {
264 None
265 } else {
266 Some(LinkBlock::new(Link::new("", "Sections"), "top-toc", links))
267 }
268}
269
270fn sidebar_struct<'a>(
271 cx: &'a Context<'_>,
272 it: &'a clean::Item,
273 s: &'a clean::Struct,
274 items: &mut Vec<LinkBlock<'a>>,
275 deref_id_map: &'a DefIdMap<String>,
276) {
277 let fields = get_struct_fields_name(&s.fields);
278 let field_name = match s.ctor_kind {
279 Some(CtorKind::Fn) => Some("Tuple Fields"),
280 None => Some("Fields"),
281 _ => None,
282 };
283 if let Some(name) = field_name {
284 items.push(LinkBlock::new(Link::new("fields", name), "structfield", fields));
285 }
286 sidebar_assoc_items(cx, it, items, deref_id_map);
287}
288
289fn sidebar_trait<'a>(
290 cx: &'a Context<'_>,
291 it: &'a clean::Item,
292 t: &'a clean::Trait,
293 blocks: &mut Vec<LinkBlock<'a>>,
294 deref_id_map: &'a DefIdMap<String>,
295) {
296 fn filter_items<'a>(
297 items: &'a [clean::Item],
298 filt: impl Fn(&clean::Item) -> bool,
299 ty: &str,
300 ) -> Vec<Link<'a>> {
301 let mut res = items
302 .iter()
303 .filter_map(|m: &clean::Item| match m.name {
304 Some(ref name) if filt(m) => Some(Link::new(format!("{ty}.{name}"), name.as_str())),
305 _ => None,
306 })
307 .collect::<Vec<Link<'a>>>();
308 res.sort();
309 res
310 }
311
312 let req_assoc = filter_items(&t.items, |m| m.is_required_associated_type(), "associatedtype");
313 let prov_assoc = filter_items(&t.items, |m| m.is_associated_type(), "associatedtype");
314 let req_assoc_const =
315 filter_items(&t.items, |m| m.is_required_associated_const(), "associatedconstant");
316 let prov_assoc_const =
317 filter_items(&t.items, |m| m.is_associated_const(), "associatedconstant");
318 let req_method = filter_items(&t.items, |m| m.is_ty_method(), "tymethod");
319 let prov_method = filter_items(&t.items, |m| m.is_method(), "method");
320 let mut foreign_impls = vec![];
321 if let Some(implementors) = cx.cache().implementors.get(&it.item_id.expect_def_id()) {
322 foreign_impls.extend(
323 implementors
324 .iter()
325 .filter(|i| !i.is_on_local_type(cx))
326 .filter_map(|i| super::extract_for_impl_name(&i.impl_item, cx))
327 .map(|(name, id)| Link::new(id, name)),
328 );
329 foreign_impls.sort();
330 }
331
332 blocks.extend(
333 [
334 ("required-associated-consts", "Required Associated Constants", req_assoc_const),
335 ("provided-associated-consts", "Provided Associated Constants", prov_assoc_const),
336 ("required-associated-types", "Required Associated Types", req_assoc),
337 ("provided-associated-types", "Provided Associated Types", prov_assoc),
338 ("required-methods", "Required Methods", req_method),
339 ("provided-methods", "Provided Methods", prov_method),
340 ("foreign-impls", "Implementations on Foreign Types", foreign_impls),
341 ]
342 .into_iter()
343 .map(|(id, title, items)| LinkBlock::new(Link::new(id, title), "", items)),
344 );
345 sidebar_assoc_items(cx, it, blocks, deref_id_map);
346
347 if !t.is_dyn_compatible(cx.tcx()) {
348 blocks.push(LinkBlock::forced(
349 Link::new("dyn-compatibility", "Dyn Compatibility"),
350 "dyn-compatibility-note",
351 ));
352 }
353
354 blocks.push(LinkBlock::forced(Link::new("implementors", "Implementors"), "impl"));
355 if t.is_auto(cx.tcx()) {
356 blocks.push(LinkBlock::forced(
357 Link::new("synthetic-implementors", "Auto Implementors"),
358 "impl-auto",
359 ));
360 }
361}
362
363fn sidebar_primitive<'a>(
364 cx: &'a Context<'_>,
365 it: &'a clean::Item,
366 items: &mut Vec<LinkBlock<'a>>,
367 deref_id_map: &'a DefIdMap<String>,
368) {
369 if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) {
370 sidebar_assoc_items(cx, it, items, deref_id_map);
371 } else {
372 let (concrete, synthetic, blanket_impl) =
373 super::get_filtered_impls_for_reference(&cx.shared, it);
374
375 sidebar_render_assoc_items(cx, &mut IdMap::new(), concrete, synthetic, blanket_impl, items);
376 }
377}
378
379fn sidebar_type_alias<'a>(
380 cx: &'a Context<'_>,
381 it: &'a clean::Item,
382 t: &'a clean::TypeAlias,
383 items: &mut Vec<LinkBlock<'a>>,
384 deref_id_map: &'a DefIdMap<String>,
385) {
386 if let Some(inner_type) = &t.inner_type {
387 items.push(LinkBlock::forced(Link::new("aliased-type", "Aliased Type"), "type"));
388 match inner_type {
389 clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive: _ } => {
390 let mut variants = variants
391 .iter()
392 .filter(|i| !i.is_stripped())
393 .filter_map(|v| v.name)
394 .map(|name| Link::new(format!("variant.{name}"), name.to_string()))
395 .collect::<Vec<_>>();
396 variants.sort_unstable();
397
398 items.push(LinkBlock::new(Link::new("variants", "Variants"), "variant", variants));
399 }
400 clean::TypeAliasInnerType::Union { fields }
401 | clean::TypeAliasInnerType::Struct { ctor_kind: _, fields } => {
402 let fields = get_struct_fields_name(fields);
403 items.push(LinkBlock::new(Link::new("fields", "Fields"), "field", fields));
404 }
405 }
406 }
407 sidebar_assoc_items(cx, it, items, deref_id_map);
408}
409
410fn sidebar_union<'a>(
411 cx: &'a Context<'_>,
412 it: &'a clean::Item,
413 u: &'a clean::Union,
414 items: &mut Vec<LinkBlock<'a>>,
415 deref_id_map: &'a DefIdMap<String>,
416) {
417 let fields = get_struct_fields_name(&u.fields);
418 items.push(LinkBlock::new(Link::new("fields", "Fields"), "structfield", fields));
419 sidebar_assoc_items(cx, it, items, deref_id_map);
420}
421
422fn sidebar_assoc_items<'a>(
424 cx: &'a Context<'_>,
425 it: &'a clean::Item,
426 links: &mut Vec<LinkBlock<'a>>,
427 deref_id_map: &'a DefIdMap<String>,
428) {
429 let did = it.item_id.expect_def_id();
430 let cache = cx.cache();
431
432 let mut assoc_consts = Vec::new();
433 let mut assoc_types = Vec::new();
434 let mut methods = Vec::new();
435 if let Some(v) = cache.impls.get(&did) {
436 let mut used_links = FxHashSet::default();
437 let mut id_map = IdMap::new();
438
439 {
440 let used_links_bor = &mut used_links;
441 for impl_ in v.iter().map(|i| i.inner_impl()).filter(|i| i.trait_.is_none()) {
442 assoc_consts.extend(get_associated_constants(impl_, used_links_bor));
443 assoc_types.extend(get_associated_types(impl_, used_links_bor));
444 methods.extend(get_methods(impl_, false, used_links_bor, false, cx.tcx()));
445 }
446 assoc_consts.sort();
448 assoc_types.sort();
449 methods.sort();
450 }
451
452 let mut blocks = vec![
453 LinkBlock::new(
454 Link::new("implementations", "Associated Constants"),
455 "associatedconstant",
456 assoc_consts,
457 ),
458 LinkBlock::new(
459 Link::new("implementations", "Associated Types"),
460 "associatedtype",
461 assoc_types,
462 ),
463 LinkBlock::new(Link::new("implementations", "Methods"), "method", methods),
464 ];
465
466 if v.iter().any(|i| i.inner_impl().trait_.is_some()) {
467 if let Some(impl_) =
468 v.iter().find(|i| i.trait_did() == cx.tcx().lang_items().deref_trait())
469 {
470 let mut derefs = DefIdSet::default();
471 derefs.insert(did);
472 sidebar_deref_methods(
473 cx,
474 &mut blocks,
475 impl_,
476 v,
477 &mut derefs,
478 &mut used_links,
479 deref_id_map,
480 );
481 }
482
483 let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
484 v.iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_auto());
485 let (blanket_impl, concrete): (Vec<&Impl>, Vec<&Impl>) =
486 concrete.into_iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_blanket());
487
488 sidebar_render_assoc_items(
489 cx,
490 &mut id_map,
491 concrete,
492 synthetic,
493 blanket_impl,
494 &mut blocks,
495 );
496 }
497
498 links.append(&mut blocks);
499 }
500}
501
502fn sidebar_deref_methods<'a>(
503 cx: &'a Context<'_>,
504 out: &mut Vec<LinkBlock<'a>>,
505 impl_: &Impl,
506 v: &[Impl],
507 derefs: &mut DefIdSet,
508 used_links: &mut FxHashSet<String>,
509 deref_id_map: &'a DefIdMap<String>,
510) {
511 let c = cx.cache();
512
513 debug!("found Deref: {impl_:?}");
514 if let Some((target, real_target)) =
515 impl_.inner_impl().items.iter().find_map(|item| match item.kind {
516 clean::AssocTypeItem(box ref t, _) => Some(match *t {
517 clean::TypeAlias { item_type: Some(ref type_), .. } => (type_, &t.type_),
518 _ => (&t.type_, &t.type_),
519 }),
520 _ => None,
521 })
522 {
523 debug!("found target, real_target: {target:?} {real_target:?}");
524 if let Some(did) = target.def_id(c) &&
525 let Some(type_did) = impl_.inner_impl().for_.def_id(c) &&
526 (did == type_did || !derefs.insert(did))
528 {
529 return;
531 }
532 let deref_mut = v.iter().any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait());
533 let inner_impl = target
534 .def_id(c)
535 .or_else(|| {
536 target.primitive_type().and_then(|prim| c.primitive_locations.get(&prim).cloned())
537 })
538 .and_then(|did| c.impls.get(&did));
539 if let Some(impls) = inner_impl {
540 debug!("found inner_impl: {impls:?}");
541 let mut ret = impls
542 .iter()
543 .filter(|i| {
544 i.inner_impl().trait_.is_none()
545 && real_target.is_doc_subtype_of(&i.inner_impl().for_, c)
546 })
547 .flat_map(|i| get_methods(i.inner_impl(), true, used_links, deref_mut, cx.tcx()))
548 .collect::<Vec<_>>();
549 if !ret.is_empty() {
550 let id = if let Some(target_def_id) = real_target.def_id(c) {
551 Cow::Borrowed(
552 deref_id_map
553 .get(&target_def_id)
554 .expect("Deref section without derived id")
555 .as_str(),
556 )
557 } else {
558 Cow::Borrowed("deref-methods")
559 };
560 let title = format!(
561 "Methods from {:#}<Target={:#}>",
562 print_path(impl_.inner_impl().trait_.as_ref().unwrap(), cx),
563 print_type(real_target, cx),
564 );
565 ret.sort();
567 out.push(LinkBlock::new(Link::new(id, title), "deref-methods", ret));
568 }
569 }
570
571 if let Some(target_did) = target.def_id(c)
573 && let Some(target_impls) = c.impls.get(&target_did)
574 && let Some(target_deref_impl) = target_impls.iter().find(|i| {
575 i.inner_impl()
576 .trait_
577 .as_ref()
578 .map(|t| Some(t.def_id()) == cx.tcx().lang_items().deref_trait())
579 .unwrap_or(false)
580 })
581 {
582 sidebar_deref_methods(
583 cx,
584 out,
585 target_deref_impl,
586 target_impls,
587 derefs,
588 used_links,
589 deref_id_map,
590 );
591 }
592 }
593}
594
595fn sidebar_enum<'a>(
596 cx: &'a Context<'_>,
597 it: &'a clean::Item,
598 e: &'a clean::Enum,
599 items: &mut Vec<LinkBlock<'a>>,
600 deref_id_map: &'a DefIdMap<String>,
601) {
602 let mut variants = e
603 .non_stripped_variants()
604 .filter_map(|v| v.name)
605 .map(|name| Link::new(format!("variant.{name}"), name.to_string()))
606 .collect::<Vec<_>>();
607 variants.sort_unstable();
608
609 items.push(LinkBlock::new(Link::new("variants", "Variants"), "variant", variants));
610 sidebar_assoc_items(cx, it, items, deref_id_map);
611}
612
613pub(crate) fn sidebar_module_like(
614 item_sections_in_use: FxHashSet<ItemSection>,
615 ids: &mut IdMap,
616 module_like: ModuleLike,
617) -> LinkBlock<'static> {
618 let item_sections: Vec<Link<'_>> = ItemSection::ALL
619 .iter()
620 .copied()
621 .filter(|sec| item_sections_in_use.contains(sec))
622 .map(|sec| Link::new(ids.derive(sec.id()), sec.name()))
623 .collect();
624 let header = if let Some(first_section) = item_sections.first() {
625 Link::new(
626 first_section.href.clone(),
627 if module_like.is_crate() { "Crate Items" } else { "Module Items" },
628 )
629 } else {
630 Link::empty()
631 };
632 LinkBlock::new(header, "", item_sections)
633}
634
635fn sidebar_module(
636 items: &[clean::Item],
637 ids: &mut IdMap,
638 module_like: ModuleLike,
639) -> LinkBlock<'static> {
640 let item_sections_in_use: FxHashSet<_> = items
641 .iter()
642 .filter(|it| {
643 !it.is_stripped()
644 && it
645 .name
646 .or_else(|| {
647 if let clean::ImportItem(ref i) = it.kind
648 && let clean::ImportKind::Simple(s) = i.kind
649 {
650 Some(s)
651 } else {
652 None
653 }
654 })
655 .is_some()
656 })
657 .map(|it| item_ty_to_section(it.type_()))
658 .collect();
659
660 sidebar_module_like(item_sections_in_use, ids, module_like)
661}
662
663fn sidebar_foreign_type<'a>(
664 cx: &'a Context<'_>,
665 it: &'a clean::Item,
666 items: &mut Vec<LinkBlock<'a>>,
667 deref_id_map: &'a DefIdMap<String>,
668) {
669 sidebar_assoc_items(cx, it, items, deref_id_map);
670}
671
672fn sidebar_render_assoc_items(
674 cx: &Context<'_>,
675 id_map: &mut IdMap,
676 concrete: Vec<&Impl>,
677 synthetic: Vec<&Impl>,
678 blanket_impl: Vec<&Impl>,
679 items: &mut Vec<LinkBlock<'_>>,
680) {
681 let format_impls = |impls: Vec<&Impl>, id_map: &mut IdMap| {
682 let mut links = FxHashSet::default();
683
684 let mut ret = impls
685 .iter()
686 .filter_map(|it| {
687 let trait_ = it.inner_impl().trait_.as_ref()?;
688 let encoded = id_map.derive(super::get_id_for_impl(cx.tcx(), it.impl_item.item_id));
689
690 let prefix = match it.inner_impl().polarity {
691 ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "",
692 ty::ImplPolarity::Negative => "!",
693 };
694 let generated = Link::new(encoded, format!("{prefix}{:#}", print_path(trait_, cx)));
695 if links.insert(generated.clone()) { Some(generated) } else { None }
696 })
697 .collect::<Vec<Link<'static>>>();
698 ret.sort();
699 ret
700 };
701
702 let concrete = format_impls(concrete, id_map);
703 let synthetic = format_impls(synthetic, id_map);
704 let blanket = format_impls(blanket_impl, id_map);
705 items.extend([
706 LinkBlock::new(
707 Link::new("trait-implementations", "Trait Implementations"),
708 "trait-implementation",
709 concrete,
710 ),
711 LinkBlock::new(
712 Link::new("synthetic-implementations", "Auto Trait Implementations"),
713 "synthetic-implementation",
714 synthetic,
715 ),
716 LinkBlock::new(
717 Link::new("blanket-implementations", "Blanket Implementations"),
718 "blanket-implementation",
719 blanket,
720 ),
721 ]);
722}
723
724fn get_next_url(used_links: &mut FxHashSet<String>, url: String) -> String {
725 if used_links.insert(url.clone()) {
726 return url;
727 }
728 let mut add = 1;
729 while !used_links.insert(format!("{url}-{add}")) {
730 add += 1;
731 }
732 format!("{url}-{add}")
733}
734
735fn get_methods<'a>(
736 i: &'a clean::Impl,
737 for_deref: bool,
738 used_links: &mut FxHashSet<String>,
739 deref_mut: bool,
740 tcx: TyCtxt<'_>,
741) -> Vec<Link<'a>> {
742 i.items
743 .iter()
744 .filter_map(|item| {
745 if let Some(ref name) = item.name
746 && item.is_method()
747 && (!for_deref || super::should_render_item(item, deref_mut, tcx))
748 {
749 Some(Link::new(
750 get_next_url(used_links, format!("{typ}.{name}", typ = ItemType::Method)),
751 name.as_str(),
752 ))
753 } else {
754 None
755 }
756 })
757 .collect()
758}
759
760fn get_associated_constants<'a>(
761 i: &'a clean::Impl,
762 used_links: &mut FxHashSet<String>,
763) -> Vec<Link<'a>> {
764 i.items
765 .iter()
766 .filter_map(|item| {
767 if let Some(ref name) = item.name
768 && item.is_associated_const()
769 {
770 Some(Link::new(
771 get_next_url(used_links, format!("{typ}.{name}", typ = ItemType::AssocConst)),
772 name.as_str(),
773 ))
774 } else {
775 None
776 }
777 })
778 .collect()
779}
780
781fn get_associated_types<'a>(
782 i: &'a clean::Impl,
783 used_links: &mut FxHashSet<String>,
784) -> Vec<Link<'a>> {
785 i.items
786 .iter()
787 .filter_map(|item| {
788 if let Some(ref name) = item.name
789 && item.is_associated_type()
790 {
791 Some(Link::new(
792 get_next_url(used_links, format!("{typ}.{name}", typ = ItemType::AssocType)),
793 name.as_str(),
794 ))
795 } else {
796 None
797 }
798 })
799 .collect()
800}