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