1use std::collections::{HashMap, HashSet};
7use std::fmt::Write;
8use std::path::Path;
9use std::sync::Arc;
10
11use fs_err as fs;
12use indicatif::{ProgressBar, ProgressStyle};
13use rayon::prelude::*;
14use rustdoc_types::{Id, Item, ItemEnum, StructKind};
15use tracing::{debug, info, info_span, instrument};
16
17use crate::Args;
18use crate::error::Error;
19use crate::generator::breadcrumbs::BreadcrumbGenerator;
20use crate::generator::impls::is_blanket_impl;
21use crate::generator::render_shared::{
22 append_docs, impl_sort_key, render_constant_definition, render_enum_definition,
23 render_enum_variants_docs, render_function_definition, render_impl_items, render_macro_heading,
24 render_struct_definition, render_struct_fields, render_trait_definition, render_trait_item,
25 render_type_alias_definition,
26};
27use crate::generator::{ItemFilter, LinkResolver};
28use crate::multi_crate::context::SingleCrateView;
29use crate::multi_crate::search::SearchIndexGenerator;
30use crate::multi_crate::summary::SummaryGenerator;
31use crate::multi_crate::{CrateCollection, MultiCrateContext};
32use crate::types::TypeRenderer;
33
34pub struct MultiCrateGenerator<'a> {
54 ctx: MultiCrateContext<'a>,
56
57 args: &'a Args,
59}
60
61impl<'a> MultiCrateGenerator<'a> {
62 #[must_use]
69 pub fn new(crates: &'a CrateCollection, args: &'a Args) -> Self {
70 let ctx = MultiCrateContext::new(crates, args);
71 Self { ctx, args }
72 }
73
74 #[instrument(skip(self), fields(
84 crate_count = self.ctx.crates().names().len(),
85 output = %self.args.output.display(),
86 mdbook = self.args.mdbook,
87 search_index = self.args.search_index
88 ))]
89 pub fn generate(&self) -> Result<(), Error> {
90 info!("Starting multi-crate documentation generation");
91
92 fs::create_dir_all(&self.args.output).map_err(Error::CreateDir)?;
94 debug!(path = %self.args.output.display(), "Created output directory");
95
96 for crate_name in self.ctx.crates().names() {
98 let crate_dir = self.args.output.join(crate_name);
99 fs::create_dir_all(&crate_dir).map_err(Error::CreateDir)?;
100 }
101
102 let total_modules: usize = self
104 .ctx
105 .crates()
106 .iter()
107 .filter_map(|(name, _)| self.ctx.single_crate_view(name))
108 .map(|view| view.count_modules() + 1)
109 .sum();
110
111 debug!(total_modules, "Total modules to generate");
112 let progress = Arc::new(Self::create_progress_bar(total_modules)?);
113
114 self.ctx
116 .crates()
117 .names()
118 .par_iter()
119 .try_for_each(|crate_name| {
120 let span = info_span!("generate_crate", crate_name);
121 let _guard = span.enter();
122
123 let view = self
124 .ctx
125 .single_crate_view(crate_name)
126 .ok_or_else(|| Error::ItemNotFound((*crate_name).clone()))?;
127
128 self.generate_crate(&view, &progress)
129 })?;
130
131 if self.args.mdbook {
133 info!("Generating SUMMARY.md for mdBook");
134 progress.set_message("Generating SUMMARY.md...");
135 let summary_gen = SummaryGenerator::new(
136 self.ctx.crates(),
137 &self.args.output,
138 !self.args.exclude_private,
139 );
140 summary_gen.generate()?;
141 }
142
143 if self.args.search_index {
145 info!("Generating search_index.json");
146 progress.set_message("Generating search_index.json...");
147
148 let rendered_items = self.collect_rendered_items();
150
151 let search_gen = SearchIndexGenerator::new(
152 self.ctx.crates(),
153 !self.args.exclude_private,
154 rendered_items,
155 );
156 search_gen
157 .write(&self.args.output)
158 .map_err(Error::FileWrite)?;
159 }
160
161 progress.finish_with_message("Done!");
162 info!("Multi-crate documentation generation complete");
163 Ok(())
164 }
165
166 fn collect_rendered_items(&self) -> HashMap<String, HashSet<Id>> {
172 let mut result = HashMap::new();
173
174 for crate_name in self.ctx.crates().names() {
175 if let Some(view) = self.ctx.single_crate_view(crate_name) {
176 let mut ids = HashSet::new();
177 Self::collect_crate_items(&view, &mut ids);
178 result.insert(crate_name.clone(), ids);
179 }
180 }
181
182 result
183 }
184
185 fn collect_crate_items(view: &SingleCrateView, ids: &mut HashSet<Id>) {
187 let krate = view.krate();
188
189 let Some(root_item) = krate.index.get(&krate.root) else {
191 return;
192 };
193
194 Self::collect_module_items(view, root_item, ids);
196 }
197
198 fn collect_module_items(view: &SingleCrateView, item: &Item, ids: &mut HashSet<Id>) {
200 let krate = view.krate();
201
202 if let ItemEnum::Module(module) = &item.inner {
203 for item_id in &module.items {
204 if let Some(child) = krate.index.get(item_id) {
205 if !view.should_include_item(child) {
206 continue;
207 }
208
209 match &child.inner {
210 ItemEnum::Struct(_)
212 | ItemEnum::Enum(_)
213 | ItemEnum::Trait(_)
214 | ItemEnum::Function(_)
215 | ItemEnum::TypeAlias(_)
216 | ItemEnum::Constant { .. }
217 | ItemEnum::Macro(_) => {
218 ids.insert(*item_id);
219 },
220
221 ItemEnum::Module(_) => {
223 ids.insert(*item_id);
224 Self::collect_module_items(view, child, ids);
225 },
226
227 ItemEnum::Use(use_item) if !use_item.is_glob => {
229 let target_exists =
231 use_item.id.as_ref().is_some_and(|target_id| {
232 krate.index.contains_key(target_id)
233 || view.lookup_item_across_crates(target_id).is_some()
234 }) || view.resolve_external_path(&use_item.source).is_some();
235
236 if target_exists {
237 ids.insert(*item_id);
238 }
239 },
240
241 _ => {},
242 }
243 }
244 }
245 }
246 }
247
248 #[instrument(skip(self, view, progress), fields(crate_name = %view.crate_name()))]
250 fn generate_crate(
251 &self,
252 view: &SingleCrateView,
253 progress: &Arc<ProgressBar>,
254 ) -> Result<(), Error> {
255 debug!("Starting crate generation");
256
257 let crate_name = view.crate_name();
258 let crate_dir = self.args.output.join(crate_name);
259
260 let root_item = view
264 .krate()
265 .index
266 .get(&view.krate().root)
267 .ok_or_else(|| Error::ItemNotFound(view.krate().root.0.to_string()))?;
268
269 let file_path = format!("{crate_name}/index.md");
271 let renderer = MultiCrateModuleRenderer::new(view, &file_path, true);
272 let content = renderer.render(root_item);
273
274 let index_path = crate_dir.join("index.md");
275 fs::write(&index_path, content).map_err(Error::FileWrite)?;
276 progress.inc(1);
277
278 if let ItemEnum::Module(module) = &root_item.inner {
280 for item_id in &module.items {
281 if let Some(item) = view.krate().index.get(item_id)
282 && let ItemEnum::Module(_) = &item.inner
283 && view.should_include_item(item)
284 {
285 Self::generate_module(view, item, &crate_dir, vec![], &Arc::clone(progress))?;
286 }
287 }
288 }
289
290 debug!("Crate generation complete");
291 Ok(())
292 }
293
294 fn generate_module(
296 view: &SingleCrateView,
297 item: &Item,
298 parent_dir: &Path,
299 module_path: Vec<String>,
300 progress: &Arc<ProgressBar>,
301 ) -> Result<(), Error> {
302 let name = item.name.as_deref().unwrap_or("unnamed");
303
304 let module_dir = parent_dir.join(name);
306 fs::create_dir_all(&module_dir).map_err(Error::CreateDir)?;
307
308 let mut current_path = module_path;
310 current_path.push(name.to_string());
311
312 let file_path = format!("{}/{}/index.md", view.crate_name(), current_path.join("/"));
314
315 let breadcrumb_gen = BreadcrumbGenerator::new(¤t_path, view.crate_name());
317 let breadcrumbs = breadcrumb_gen.generate();
318
319 let renderer = MultiCrateModuleRenderer::new(view, &file_path, false);
321 let module_content = renderer.render(item);
322
323 let content = format!("{breadcrumbs}{module_content}");
325
326 let file_path_on_disk = module_dir.join("index.md");
328 fs::write(&file_path_on_disk, content).map_err(Error::FileWrite)?;
329 progress.inc(1);
330
331 if let ItemEnum::Module(module) = &item.inner {
333 for sub_id in &module.items {
334 if let Some(sub_item) = view.krate().index.get(sub_id)
335 && let ItemEnum::Module(_) = &sub_item.inner
336 && view.should_include_item(sub_item)
337 {
338 Self::generate_module(
339 view,
340 sub_item,
341 &module_dir,
342 current_path.clone(),
343 &Arc::clone(progress),
344 )?;
345 }
346 }
347 }
348
349 Ok(())
350 }
351
352 fn create_progress_bar(total: usize) -> Result<ProgressBar, Error> {
358 let progress = ProgressBar::new(total as u64);
359
360 let style =
361 ProgressStyle::with_template("{spinner:.green} [{bar:40.cyan/blue}] {pos}/{len} {msg}")
362 .map_err(Error::ProgressBarTemplate)?
363 .progress_chars("=>-");
364
365 progress.set_style(style);
366 Ok(progress)
367 }
368}
369
370struct MultiCrateModuleRenderer<'a> {
378 view: &'a SingleCrateView<'a>,
380
381 file_path: &'a str,
383
384 is_root: bool,
386
387 type_renderer: TypeRenderer<'a>,
389}
390
391impl<'a> MultiCrateModuleRenderer<'a> {
392 const fn new(view: &'a SingleCrateView<'a>, file_path: &'a str, is_root: bool) -> Self {
394 Self {
395 view,
396 file_path,
397 is_root,
398 type_renderer: TypeRenderer::new(view.krate()),
399 }
400 }
401
402 fn render(&self, item: &Item) -> String {
404 let mut md = String::new();
405
406 let name = item.name.as_deref().unwrap_or("unnamed");
408 if self.is_root {
409 _ = writeln!(md, "# Crate `{name}`\n");
410 } else {
411 _ = writeln!(md, "# Module `{name}`\n");
412 }
413
414 if let Some(docs) = self.view.process_docs(item, self.file_path) {
416 _ = writeln!(md, "{docs}\n");
417 }
418
419 if let ItemEnum::Module(module) = &item.inner {
421 self.render_module_contents(&mut md, module, item);
422 }
423
424 md
425 }
426
427 fn render_module_contents(
429 &self,
430 md: &mut String,
431 module: &rustdoc_types::Module,
432 _parent: &Item,
433 ) {
434 let krate = self.view.krate();
435 let mut seen_items: HashSet<Id> = HashSet::new();
436
437 let mut modules: Vec<&Item> = Vec::new();
439 let mut structs: Vec<(&Id, &Item)> = Vec::new();
440 let mut enums: Vec<(&Id, &Item)> = Vec::new();
441 let mut traits: Vec<&Item> = Vec::new();
442 let mut functions: Vec<&Item> = Vec::new();
443 let mut types: Vec<&Item> = Vec::new();
444 let mut constants: Vec<&Item> = Vec::new();
445 let mut macros: Vec<&Item> = Vec::new();
446
447 for item_id in &module.items {
448 if !seen_items.insert(*item_id) {
450 continue;
451 }
452
453 if let Some(item) = krate.index.get(item_id) {
454 if !self.view.should_include_item(item) {
455 continue;
456 }
457
458 match &item.inner {
459 ItemEnum::Module(_) => modules.push(item),
460
461 ItemEnum::Struct(_) => structs.push((item_id, item)),
462
463 ItemEnum::Enum(_) => enums.push((item_id, item)),
464
465 ItemEnum::Trait(_) => traits.push(item),
466
467 ItemEnum::Function(_) => functions.push(item),
468
469 ItemEnum::TypeAlias(_) => types.push(item),
470
471 ItemEnum::Constant { .. } => constants.push(item),
472
473 ItemEnum::Macro(_) => macros.push(item),
474
475 ItemEnum::Use(use_item) => {
477 if use_item.is_glob {
478 self.expand_glob_reexport(
480 &mut modules,
481 &mut structs,
482 &mut enums,
483 &mut traits,
484 &mut functions,
485 &mut types,
486 &mut constants,
487 &mut macros,
488 use_item,
489 &mut seen_items,
490 );
491 } else {
492 let target_item = use_item.id.as_ref().map_or_else(
494 || {
495 self.view
497 .resolve_external_path(&use_item.source)
498 .map(|(_, item, _)| item)
499 },
500 |target_id| {
501 krate.index.get(target_id).or_else(|| {
503 self.view
504 .lookup_item_across_crates(target_id)
505 .map(|(_, item)| item)
506 })
507 },
508 );
509
510 if let Some(target_item) = target_item {
511 match &target_item.inner {
512 ItemEnum::Module(_) => modules.push(item),
513
514 ItemEnum::Struct(_) => structs.push((item_id, item)),
515
516 ItemEnum::Enum(_) => enums.push((item_id, item)),
517
518 ItemEnum::Trait(_) => traits.push(item),
519
520 ItemEnum::Function(_) => functions.push(item),
521
522 ItemEnum::TypeAlias(_) => types.push(item),
523
524 ItemEnum::Constant { .. } => constants.push(item),
525
526 ItemEnum::Macro(_) => macros.push(item),
527
528 _ => {},
529 }
530 }
531 }
532 },
533 _ => {},
534 }
535 }
536 }
537
538 let is_empty = modules.is_empty()
540 && structs.is_empty()
541 && enums.is_empty()
542 && traits.is_empty()
543 && functions.is_empty()
544 && types.is_empty()
545 && constants.is_empty()
546 && macros.is_empty();
547
548 if is_empty && self.is_root {
549 let crate_name = self.view.crate_name();
551 if crate_name.ends_with("_derive") || crate_name.ends_with("-derive") {
552 let parent_crate = crate_name
554 .strip_suffix("_derive")
555 .or_else(|| crate_name.strip_suffix("-derive"))
556 .unwrap_or(crate_name);
557
558 _ = writeln!(md, "## Overview\n");
559 _ = writeln!(
560 md,
561 "This is a **procedural macro crate** that provides derive macros."
562 );
563 _ = writeln!(md);
564 _ = writeln!(
565 md,
566 "The macros from this crate are typically re-exported from the parent crate \
567 [`{parent_crate}`](../{parent_crate}/index.md) for convenience. \
568 You should generally depend on the parent crate rather than this one directly."
569 );
570 _ = writeln!(md);
571 _ = writeln!(md, "### Usage\n");
572 _ = writeln!(md, "```toml");
573 _ = writeln!(md, "[dependencies]");
574 _ = writeln!(
575 md,
576 "{parent_crate} = {{ version = \"*\", features = [\"derive\"] }}"
577 );
578 _ = writeln!(md, "```");
579 } else if crate_name.ends_with("_impl") || crate_name.ends_with("-impl") {
580 let parent_crate = crate_name
581 .strip_suffix("_impl")
582 .or_else(|| crate_name.strip_suffix("-impl"))
583 .unwrap_or(crate_name);
584
585 _ = writeln!(md, "## Overview\n");
586 _ = writeln!(
587 md,
588 "This is an **implementation detail crate** with no public API."
589 );
590 _ = writeln!(md);
591 _ = writeln!(
592 md,
593 "The functionality from this crate is re-exported through \
594 [`{parent_crate}`](../{parent_crate}/index.md). \
595 You should depend on the parent crate instead."
596 );
597 } else {
598 _ = writeln!(md, "*This crate has no public items to document.*");
599 }
600 return;
601 }
602
603 Self::render_modules_section(md, &modules);
605 self.render_structs_section(md, &structs);
606 self.render_enums_section(md, &enums);
607 self.render_traits_section(md, &traits);
608 self.render_functions_section(md, &functions);
609 self.render_type_aliases_section(md, &types);
610 self.render_constants_section(md, &constants);
611 self.render_macros_section(md, ¯os);
612 }
613
614 fn render_modules_section(md: &mut String, modules: &[&Item]) {
616 if modules.is_empty() {
617 return;
618 }
619
620 _ = writeln!(md, "## Modules\n");
621
622 for item in modules {
623 let (name, summary) = Self::get_item_name_and_summary(item);
624 _ = writeln!(md, "- [`{name}`]({name}/index.md) - {summary}");
625 }
626
627 _ = writeln!(md);
628 }
629
630 fn render_structs_section(&self, md: &mut String, structs: &[(&Id, &Item)]) {
632 if structs.is_empty() {
633 return;
634 }
635
636 _ = writeln!(md, "## Structs\n");
637
638 for (item_id, item) in structs {
639 self.render_struct(md, **item_id, item);
640 }
641 }
642
643 fn render_enums_section(&self, md: &mut String, enums: &[(&Id, &Item)]) {
645 if enums.is_empty() {
646 return;
647 }
648
649 _ = writeln!(md, "## Enums\n");
650
651 for (item_id, item) in enums {
652 self.render_enum(md, **item_id, item);
653 }
654 }
655
656 fn render_traits_section(&self, md: &mut String, traits: &[&Item]) {
658 if traits.is_empty() {
659 return;
660 }
661
662 _ = writeln!(md, "## Traits\n");
663
664 for item in traits {
665 self.render_trait(md, item);
666 }
667 }
668
669 fn render_functions_section(&self, md: &mut String, functions: &[&Item]) {
671 if functions.is_empty() {
672 return;
673 }
674
675 _ = writeln!(md, "## Functions\n");
676
677 for item in functions {
678 self.render_function(md, item);
679 }
680 }
681
682 fn render_type_aliases_section(&self, md: &mut String, types: &[&Item]) {
684 if types.is_empty() {
685 return;
686 }
687
688 _ = writeln!(md, "## Type Aliases\n");
689
690 for item in types {
691 self.render_type_alias(md, item);
692 }
693 }
694
695 fn render_constants_section(&self, md: &mut String, constants: &[&Item]) {
697 if constants.is_empty() {
698 return;
699 }
700
701 _ = writeln!(md, "## Constants\n");
702
703 for item in constants {
704 self.render_constant(md, item);
705 }
706 }
707
708 fn render_macros_section(&self, md: &mut String, macros: &[&Item]) {
710 if macros.is_empty() {
711 return;
712 }
713
714 _ = writeln!(md, "## Macros\n");
715
716 for item in macros {
717 self.render_macro(md, item);
718 }
719 }
720
721 fn get_item_name_and_summary(item: &Item) -> (String, String) {
723 if let ItemEnum::Use(use_item) = &item.inner {
724 let name = use_item.name.clone();
726 let docs = item.docs.as_deref().unwrap_or("");
727 let summary = docs.lines().next().unwrap_or("").to_string();
728 (name, summary)
729 } else {
730 let name = item.name.clone().unwrap_or_else(|| "unnamed".to_string());
731 let docs = item.docs.as_deref().unwrap_or("");
732 let summary = docs.lines().next().unwrap_or("").to_string();
733 (name, summary)
734 }
735 }
736
737 fn render_struct(&self, md: &mut String, item_id: Id, item: &Item) {
739 let current_krate = self.view.krate();
740
741 let (name, actual_item, actual_id, source_crate_name): (&str, &Item, Id, Option<&str>) =
744 if let ItemEnum::Use(use_item) = &item.inner {
745 let name = use_item.name.as_str();
746
747 if let Some(ref target_id) = use_item.id {
748 if let Some(target) = current_krate.index.get(target_id) {
750 (name, target, *target_id, None)
752 } else if let Some((src_crate, target)) =
753 self.view.lookup_item_across_crates(target_id)
754 {
755 let is_external = src_crate != self.view.crate_name();
757 (
758 name,
759 target,
760 *target_id,
761 if is_external { Some(src_crate) } else { None },
762 )
763 } else {
764 return;
765 }
766 } else {
767 if let Some((src_crate, target, target_id)) =
769 self.view.resolve_external_path(&use_item.source)
770 {
771 (name, target, target_id, Some(src_crate))
772 } else {
773 return;
774 }
775 }
776 } else {
777 (
778 item.name.as_deref().unwrap_or("unnamed"),
779 item,
780 item_id,
781 None,
782 )
783 };
784
785 if let ItemEnum::Struct(s) = &actual_item.inner {
786 let render_krate = source_crate_name
789 .and_then(|name| self.view.get_crate(name))
790 .unwrap_or(current_krate);
791
792 let type_renderer = if source_crate_name.is_some() {
794 TypeRenderer::new(render_krate)
795 } else {
796 self.type_renderer
798 };
799
800 render_struct_definition(md, name, s, render_krate, &type_renderer);
802
803 if let Some(src_crate) = source_crate_name {
805 _ = writeln!(md, "*Re-exported from `{src_crate}`*\n");
806 }
807
808 append_docs(md, self.view.process_docs(actual_item, self.file_path));
810
811 if let StructKind::Plain { fields, .. } = &s.kind {
813 render_struct_fields(md, fields, render_krate, &type_renderer, |field| {
814 self.view.process_docs(field, self.file_path)
815 });
816 }
817
818 self.render_impl_blocks(md, actual_id, source_crate_name);
820 }
821 }
822
823 fn render_enum(&self, md: &mut String, item_id: Id, item: &Item) {
825 let current_krate = self.view.krate();
826
827 let (name, actual_item, actual_id, source_crate_name): (&str, &Item, Id, Option<&str>) =
830 if let ItemEnum::Use(use_item) = &item.inner {
831 let name = use_item.name.as_str();
832
833 if let Some(ref target_id) = use_item.id {
834 if let Some(target) = current_krate.index.get(target_id) {
836 (name, target, *target_id, None)
838 } else if let Some((src_crate, target)) =
839 self.view.lookup_item_across_crates(target_id)
840 {
841 let is_external = src_crate != self.view.crate_name();
843 (
844 name,
845 target,
846 *target_id,
847 if is_external { Some(src_crate) } else { None },
848 )
849 } else {
850 return;
851 }
852 } else {
853 if let Some((src_crate, target, target_id)) =
855 self.view.resolve_external_path(&use_item.source)
856 {
857 (name, target, target_id, Some(src_crate))
858 } else {
859 return;
860 }
861 }
862 } else {
863 (
864 item.name.as_deref().unwrap_or("unnamed"),
865 item,
866 item_id,
867 None,
868 )
869 };
870
871 if let ItemEnum::Enum(e) = &actual_item.inner {
872 let render_krate = source_crate_name
875 .and_then(|name| self.view.get_crate(name))
876 .unwrap_or(current_krate);
877
878 let type_renderer = if source_crate_name.is_some() {
880 TypeRenderer::new(render_krate)
881 } else {
882 self.type_renderer
884 };
885
886 render_enum_definition(md, name, e, render_krate, &type_renderer);
888
889 if let Some(src_crate) = source_crate_name {
891 _ = writeln!(md, "*Re-exported from `{src_crate}`*\n");
892 }
893
894 append_docs(md, self.view.process_docs(actual_item, self.file_path));
896
897 render_enum_variants_docs(md, &e.variants, render_krate, |variant| {
899 self.view.process_docs(variant, self.file_path)
900 });
901
902 self.render_impl_blocks(md, actual_id, source_crate_name);
904 }
905 }
906
907 fn render_trait(&self, md: &mut String, item: &Item) {
909 let krate = self.view.krate();
910 let name = item.name.as_deref().unwrap_or("unnamed");
911
912 if let ItemEnum::Trait(t) = &item.inner {
913 render_trait_definition(md, name, t, &self.type_renderer);
915
916 append_docs(md, self.view.process_docs(item, self.file_path));
918
919 if !t.items.is_empty() {
921 md.push_str("#### Required Methods\n\n");
922
923 for method_id in &t.items {
924 if let Some(method) = krate.index.get(method_id) {
925 render_trait_item(md, method, &self.type_renderer, |m| {
926 self.view.process_docs(m, self.file_path)
927 });
928 }
929 }
930 }
931 }
932 }
933
934 fn render_function(&self, md: &mut String, item: &Item) {
936 let name = item.name.as_deref().unwrap_or("unnamed");
937
938 if let ItemEnum::Function(f) = &item.inner {
939 render_function_definition(md, name, f, &self.type_renderer);
940 }
941
942 append_docs(md, self.view.process_docs(item, self.file_path));
943 }
944
945 fn render_constant(&self, md: &mut String, item: &Item) {
947 let name = item.name.as_deref().unwrap_or("unnamed");
948
949 if let ItemEnum::Constant { type_, const_ } = &item.inner {
950 render_constant_definition(md, name, type_, const_, &self.type_renderer);
951 }
952
953 append_docs(md, self.view.process_docs(item, self.file_path));
954 }
955
956 fn render_type_alias(&self, md: &mut String, item: &Item) {
958 let name = item.name.as_deref().unwrap_or("unnamed");
959
960 if let ItemEnum::TypeAlias(ta) = &item.inner {
961 render_type_alias_definition(md, name, ta, &self.type_renderer);
962 }
963
964 append_docs(md, self.view.process_docs(item, self.file_path));
965 }
966
967 fn render_macro(&self, md: &mut String, item: &Item) {
969 let name = item.name.as_deref().unwrap_or("unnamed");
970 render_macro_heading(md, name);
971 append_docs(md, self.view.process_docs(item, self.file_path));
972 }
973
974 #[allow(clippy::too_many_arguments)]
976 fn expand_glob_reexport<'b>(
977 &self,
978 modules: &mut Vec<&'b Item>,
979 structs: &mut Vec<(&'b Id, &'b Item)>,
980 enums: &mut Vec<(&'b Id, &'b Item)>,
981 traits: &mut Vec<&'b Item>,
982 functions: &mut Vec<&'b Item>,
983 types: &mut Vec<&'b Item>,
984 constants: &mut Vec<&'b Item>,
985 macros: &mut Vec<&'b Item>,
986 use_item: &rustdoc_types::Use,
987 seen_items: &mut HashSet<Id>,
988 ) where
989 'a: 'b,
990 {
991 let krate = self.view.krate();
992
993 let Some(target_id) = &use_item.id else {
994 return;
995 };
996 let Some(target_module) = krate.index.get(target_id) else {
997 return;
998 };
999 let ItemEnum::Module(module) = &target_module.inner else {
1000 return;
1001 };
1002
1003 for child_id in &module.items {
1004 if !seen_items.insert(*child_id) {
1005 continue; }
1007
1008 let Some(child) = krate.index.get(child_id) else {
1009 continue;
1010 };
1011
1012 if !self.view.should_include_item(child) {
1013 continue;
1014 }
1015
1016 match &child.inner {
1017 ItemEnum::Module(_) => modules.push(child),
1018 ItemEnum::Struct(_) => structs.push((child_id, child)),
1019 ItemEnum::Enum(_) => enums.push((child_id, child)),
1020 ItemEnum::Trait(_) => traits.push(child),
1021 ItemEnum::Function(_) => functions.push(child),
1022 ItemEnum::TypeAlias(_) => types.push(child),
1023 ItemEnum::Constant { .. } => constants.push(child),
1024 ItemEnum::Macro(_) => macros.push(child),
1025 _ => {},
1026 }
1027 }
1028 }
1029
1030 fn render_impl_blocks(&self, md: &mut String, item_id: Id, source_crate_name: Option<&str>) {
1039 let current_krate = self.view.krate();
1040
1041 let render_krate = source_crate_name
1043 .and_then(|name| self.view.get_crate(name))
1044 .unwrap_or(current_krate);
1045
1046 let type_renderer = if source_crate_name.is_some() {
1048 TypeRenderer::new(render_krate)
1049 } else {
1050 self.type_renderer
1051 };
1052
1053 let impls = if source_crate_name.is_some() {
1055 self.view.get_impls_from_crate(item_id, render_krate)
1057 } else {
1058 self.view.get_all_impls(item_id)
1060 };
1061
1062 if impls.is_empty() {
1063 return;
1064 }
1065
1066 let (inherent, trait_impls): (Vec<&rustdoc_types::Impl>, Vec<&rustdoc_types::Impl>) =
1068 impls.into_iter().partition(|i| i.trait_.is_none());
1069
1070 let inherent: Vec<_> = inherent.into_iter().filter(|i| !i.is_synthetic).collect();
1072
1073 let include_blanket = self.view.include_blanket_impls();
1076 let mut trait_impls: Vec<_> = trait_impls
1077 .into_iter()
1078 .filter(|i| !i.is_synthetic)
1079 .filter(|i| include_blanket || !is_blanket_impl(i))
1080 .collect();
1081
1082 trait_impls.sort_by(|a, b| {
1084 let key_a = impl_sort_key(a, &type_renderer);
1085 let key_b = impl_sort_key(b, &type_renderer);
1086 key_a.cmp(&key_b)
1087 });
1088
1089 trait_impls
1091 .dedup_by(|a, b| impl_sort_key(a, &type_renderer) == impl_sort_key(b, &type_renderer));
1092
1093 if !inherent.is_empty() {
1095 _ = write!(md, "#### Implementations\n\n");
1096
1097 for impl_block in inherent {
1098 render_impl_items(
1099 md,
1100 impl_block,
1101 render_krate,
1102 &type_renderer,
1103 &None::<fn(&Item) -> Option<String>>,
1104 &Some(|id: rustdoc_types::Id| {
1105 LinkResolver::create_link(self.view, id, self.file_path)
1106 }),
1107 );
1108 }
1109 }
1110
1111 if !trait_impls.is_empty() {
1113 _ = write!(md, "#### Trait Implementations\n\n");
1114
1115 for impl_block in trait_impls {
1116 if let Some(trait_path) = &impl_block.trait_ {
1117 let trait_name = trait_path
1119 .path
1120 .split("::")
1121 .last()
1122 .unwrap_or(&trait_path.path);
1123
1124 let generics = type_renderer.render_generics(&impl_block.generics.params);
1125 let for_type = type_renderer.render_type(&impl_block.for_);
1126
1127 let unsafe_str = if impl_block.is_unsafe { "unsafe " } else { "" };
1129 let negative_str = if impl_block.is_negative { "!" } else { "" };
1130
1131 _ = writeln!(
1132 md,
1133 "##### `{unsafe_str}impl{generics} {negative_str}{trait_name} for {for_type}`\n"
1134 );
1135 }
1136
1137 render_impl_items(
1138 md,
1139 impl_block,
1140 render_krate,
1141 &type_renderer,
1142 &None::<fn(&Item) -> Option<String>>,
1143 &Some(|id: rustdoc_types::Id| {
1144 LinkResolver::create_link(self.view, id, self.file_path)
1145 }),
1146 );
1147 }
1148 }
1149 }
1150}