1#![allow(clippy::uninlined_format_args)]
2#![allow(clippy::too_many_lines)]
3#![allow(clippy::cognitive_complexity)] use anyhow::{Context, Result, bail}; use cargo_manifest::{FeatureSet, Manifest as CargoManifest, StringOrBool}; use graph::{Edge, IdGraph, ResolvedModule};
114use rustdoc_json::Builder;
115use rustdoc_types::{
116 Abi, Attribute, Constant, Crate, Discriminant, Enum, Function, GenericArg, GenericArgs,
117 GenericBound, GenericParamDef, Generics, Id, Impl, Item, ItemEnum, ItemKind, Path, PolyTrait,
118 Primitive, Struct, StructKind, Term, Trait, Type, Union, Variant, VariantKind, WherePredicate,
119};
120use std::collections::{HashMap, HashSet}; use std::fmt::Write as FmtWrite; use std::hash::{Hash, Hasher};
123use std::path::Path as FilePath; use tracing::{debug, info, trace, warn};
125use std::fs;
127use std::io::BufReader; use pulldown_cmark::{Event, Parser as CmarkParser, Tag, TagEnd}; use pulldown_cmark_to_cmark::cmark;
132
133pub const NIGHTLY_RUST_VERSION: &str = "nightly-2025-11-27";
135
136pub mod cratesio;
137
138#[doc(hidden)]
139pub mod graph;
140
141#[derive(Debug, Clone, Default)]
148pub struct CrateExtra {
149 pub readme_content: Option<String>,
151 pub examples_readme_content: Option<String>,
153 pub examples: Vec<(String, String)>, }
156
157#[derive(Debug, Default)]
161pub struct CrateExtraReader {
162 read_readme: bool,
163 read_examples: bool,
164}
165
166impl CrateExtraReader {
167 pub fn new() -> Self {
170 Self {
171 read_readme: true,
172 read_examples: true,
173 }
174 }
175
176 pub fn no_readme(mut self) -> Self {
178 self.read_readme = false;
179 self
180 }
181
182 pub fn no_examples(mut self) -> Self {
184 self.read_examples = false;
185 self
186 }
187
188 pub fn read(&self, manifest: &CargoManifest, package_dir: &FilePath) -> Result<CrateExtra> {
199 let mut extra = CrateExtra::default();
200
201 if self.read_readme {
202 let readme_path_from_manifest = manifest
203 .package
204 .as_ref()
205 .and_then(|p| p.readme.as_ref())
206 .and_then(|r| r.as_ref().as_local()); let readme_file_to_read = match readme_path_from_manifest {
209 Some(StringOrBool::String(readme_filename)) => {
210 Some(package_dir.join(readme_filename))
211 }
212 Some(StringOrBool::Bool(true)) | None => {
213 let default_readme_md = package_dir.join("README.md");
215 if default_readme_md.exists() {
216 Some(default_readme_md)
217 } else {
218 Some(package_dir.join("README"))
220 }
221 }
222 Some(StringOrBool::Bool(false)) => {
223 info!("README explicitly disabled in Cargo.toml.");
224 None
225 }
226 };
227
228 if let Some(path) = readme_file_to_read {
229 if path.exists() {
230 match fs::read_to_string(&path) {
231 Ok(content) => extra.readme_content = Some(content),
232 Err(_) => warn!("Failed to read README at {}", path.display()),
233 }
234 } else {
235 info!(
236 "README file specified in Cargo.toml not found: {}",
237 path.display()
238 );
239 }
240 } else if readme_path_from_manifest.is_none() {
241 info!("No README specified in Cargo.toml and no default README found.");
243 }
244 }
245
246 if self.read_examples {
247 let examples_dir = package_dir.join("examples");
248 if examples_dir.is_dir() {
249 let ex_readme_md_path = examples_dir.join("README.md");
250 let ex_readme_path = examples_dir.join("README");
251 if let Some(path) = ex_readme_md_path
252 .exists()
253 .then_some(ex_readme_md_path)
254 .or_else(|| ex_readme_path.exists().then_some(ex_readme_path))
255 && let Ok(content) = fs::read_to_string(path)
256 {
257 extra.examples_readme_content = Some(content);
258 }
259
260 if let Ok(entries) = fs::read_dir(&examples_dir) {
261 let mut found_examples = Vec::new();
262 for entry in entries.flatten() {
263 let path = entry.path();
264 if path.is_file()
265 && path.extension().is_some_and(|ext| ext == "rs")
266 && let Some(filename_str) = path.file_name().and_then(|n| n.to_str())
267 && let Ok(content) = fs::read_to_string(&path)
268 {
269 found_examples.push((filename_str.to_string(), content));
270 }
271 }
272 if !found_examples.is_empty() {
273 found_examples.sort_by(|a, b| a.0.cmp(&b.0));
274 extra.examples = found_examples;
275 }
276 }
277 }
278 }
279 Ok(extra)
280 }
281}
282
283#[derive(Debug, Clone, Default)]
286struct CrateManifestData {
287 description: Option<String>,
288 homepage: Option<String>,
289 repository: Option<String>,
290 categories: Vec<String>,
291 license: Option<String>,
292 rust_version: Option<String>,
293 edition: Option<String>,
294 features: FeatureSet, }
296
297impl CrateManifestData {
298 fn from_cargo_manifest(manifest: &CargoManifest) -> Self {
299 let package_data = manifest.package.as_ref();
300 CrateManifestData {
301 description: package_data
302 .and_then(|p| p.description.as_ref())
303 .and_then(|d| d.as_ref().as_local())
304 .cloned(),
305 homepage: package_data
306 .and_then(|p| p.homepage.as_ref())
307 .and_then(|h| h.as_ref().as_local())
308 .cloned(),
309 repository: package_data
310 .and_then(|p| p.repository.as_ref())
311 .and_then(|r| r.as_ref().as_local())
312 .cloned(),
313 categories: package_data
314 .and_then(|p| p.categories.as_ref())
315 .and_then(|c| c.as_ref().as_local())
316 .cloned()
317 .unwrap_or_default(),
318 license: package_data
319 .and_then(|p| p.license.as_ref())
320 .and_then(|l| l.as_ref().as_local())
321 .cloned(),
322 rust_version: package_data
323 .and_then(|p| p.rust_version.as_ref())
324 .and_then(|rv| rv.as_ref().as_local())
325 .cloned(),
326 edition: package_data
327 .and_then(|p| p.edition.as_ref())
328 .and_then(|e| e.as_ref().as_local())
329 .map(|e| e.as_str().to_string()),
330 features: manifest.features.clone().unwrap_or_default(),
331 }
332 }
333}
334
335pub fn run_rustdoc(
357 crate_dir: &FilePath,
358 crate_name: &str,
359 features: Option<&str>,
360 no_default_features: bool,
361 target: Option<&str>,
362 allow_rustup: bool,
363) -> Result<Crate> {
364 let manifest_path = crate_dir.join("Cargo.toml");
365 if !manifest_path.exists() {
366 bail!(
367 "Cargo.toml not found in unpacked crate at {}",
368 manifest_path.display()
369 );
370 }
371
372 if allow_rustup {
373 rustup_toolchain::install(NIGHTLY_RUST_VERSION).unwrap();
375 }
376
377 info!("Generating rustdoc JSON using rustdoc-json crate...");
378
379 let crate_name_underscore = crate_name.replace('-', "_");
380 let json_output_path = crate_dir
381 .join("target/doc")
382 .join(format!("{}.json", crate_name_underscore));
383
384 let mut builder = Builder::default()
385 .manifest_path(manifest_path)
386 .toolchain(NIGHTLY_RUST_VERSION) .target_dir(crate_dir.join("target")) .package(crate_name); if let Some(features_str) = features {
392 let feature_list: Vec<String> = features_str.split_whitespace().map(String::from).collect();
393 if !feature_list.is_empty() {
394 info!("Enabling features: {:?}", feature_list);
395 builder = builder.features(feature_list);
396 }
397 }
398
399 if no_default_features {
400 info!("Disabling default features.");
401 builder = builder.no_default_features(true);
402 }
403
404 if let Some(target_str) = target {
406 info!("Setting target: {}", target_str);
407 builder = builder.target(target_str.to_string());
408 }
409
410 match builder.build() {
412 Ok(s) => {
413 info!("Generated rustdoc JSON at: {}", s.display());
414 }
415 Err(e) => {
416 eprintln!("--- rustdoc-json build failed ---");
418 eprintln!("{:?}", e); if json_output_path.exists()
422 && let Ok(content) = std::fs::read_to_string(&json_output_path)
423 {
424 eprintln!(
425 "\n--- Potential content of {}: ---\n{}",
426 json_output_path.display(),
427 content
428 );
429 }
430
431 bail!("rustdoc-json failed: {}", e);
432 }
433 }
434
435 info!("Parsing rustdoc JSON: {}", json_output_path.display());
436 let file = fs::File::open(&json_output_path)
437 .with_context(|| format!("Failed to open JSON file: {}", json_output_path.display()))?;
438 let reader = BufReader::new(file);
439 let krate_data: Crate = serde_json::from_reader(reader)
440 .with_context(|| format!("Failed to parse JSON file: {}", json_output_path.display()))?;
441 info!(
442 "Loaded rustdoc JSON for {} v{}",
443 crate_name,
444 krate_data.crate_version.as_deref().unwrap_or("?")
445 );
446 Ok(krate_data)
447}
448
449pub(crate) fn get_type_id(ty: &Type) -> Option<Id> {
451 match ty {
452 Type::ResolvedPath(p) => Some(p.id),
453 Type::Generic(_) => None, Type::Primitive(_) => None,
455 Type::FunctionPointer(_) => None, Type::Tuple(_) => None,
457 Type::Slice(inner) => get_type_id(inner), Type::Array { type_, .. } => get_type_id(type_), Type::Pat { type_, .. } => get_type_id(type_), Type::Infer => None,
461 Type::RawPointer { type_, .. } => get_type_id(type_), Type::BorrowedRef { type_, .. } => get_type_id(type_), Type::QualifiedPath { self_type, .. } => get_type_id(self_type), Type::ImplTrait(_) => None,
465 Type::DynTrait(_) => None,
466 }
467}
468
469fn attribute_to_string(attr: &Attribute) -> String {
476 match attr {
477 Attribute::NonExhaustive => "#[non_exhaustive]".to_string(),
478 Attribute::MustUse { reason } => {
479 if let Some(r) = reason {
480 format!("#[must_use = \"{}\"]", r)
481 } else {
482 "#[must_use]".to_string()
483 }
484 }
485 Attribute::MacroExport => "#[macro_export]".to_string(),
486 Attribute::ExportName(name) => format!("#[export_name = \"{}\"]", name),
487 Attribute::LinkSection(section) => format!("#[link_section = \"{}\"]", section),
488 Attribute::AutomaticallyDerived => "#[automatically_derived]".to_string(),
489 Attribute::Repr(repr) => format!("#[repr({:?})]", repr),
490 Attribute::NoMangle => "#[no_mangle]".to_string(),
491 Attribute::TargetFeature { enable } => {
492 format!("#[target_feature(enable = \"{}\")]", enable.join("\", \""))
493 }
494 Attribute::Other(s) => s.clone(),
495 }
496}
497
498fn format_attributes(attrs: &[Attribute]) -> String {
499 let filtered_attrs: Vec<String> = attrs
500 .iter()
501 .map(attribute_to_string)
502 .filter(|attr| !attr.starts_with("#[derive("))
503 .collect();
504
505 if filtered_attrs.is_empty() {
506 String::new()
507 } else {
508 filtered_attrs
509 .iter()
510 .map(|attr| format!("{}\n", attr))
511 .collect::<String>()
512 }
513}
514
515fn format_attributes_inline(attrs: &[Attribute]) -> String {
518 let filtered_attrs: Vec<String> = attrs
519 .iter()
520 .map(attribute_to_string)
521 .filter(|attr| !attr.starts_with("#[derive("))
522 .collect();
523
524 if filtered_attrs.is_empty() {
525 String::new()
526 } else {
527 format!("{} ", filtered_attrs.join(" "))
528 }
529}
530
531fn has_docs(item: &Item) -> bool {
533 item.docs.as_ref().is_some_and(|d| !d.trim().is_empty())
534}
535
536fn adjust_markdown_headers(markdown: &str, base_level: usize) -> String {
540 let parser = CmarkParser::new(markdown);
541 let transformed_events = parser.map(|event| match event {
542 Event::Start(Tag::Heading {
543 level,
544 id,
545 classes,
546 attrs,
547 }) => {
548 let old_level_usize = match level {
550 pulldown_cmark::HeadingLevel::H1 => 1,
551 pulldown_cmark::HeadingLevel::H2 => 2,
552 pulldown_cmark::HeadingLevel::H3 => 3,
553 pulldown_cmark::HeadingLevel::H4 => 4,
554 pulldown_cmark::HeadingLevel::H5 => 5,
555 pulldown_cmark::HeadingLevel::H6 => 6,
556 };
557 let new_level_usize = std::cmp::min(old_level_usize + base_level, 6);
558 let new_level = pulldown_cmark::HeadingLevel::try_from(new_level_usize)
559 .unwrap_or(pulldown_cmark::HeadingLevel::H6);
560 Event::Start(pulldown_cmark::Tag::Heading {
561 level: new_level,
562 id,
563 classes,
564 attrs,
565 })
566 }
567 Event::End(TagEnd::Heading(level)) => {
568 let old_level_usize = match level {
570 pulldown_cmark::HeadingLevel::H1 => 1,
571 pulldown_cmark::HeadingLevel::H2 => 2,
572 pulldown_cmark::HeadingLevel::H3 => 3,
573 pulldown_cmark::HeadingLevel::H4 => 4,
574 pulldown_cmark::HeadingLevel::H5 => 5,
575 pulldown_cmark::HeadingLevel::H6 => 6,
576 };
577 let new_level_usize = std::cmp::min(old_level_usize + base_level, 6);
578 let new_level = pulldown_cmark::HeadingLevel::try_from(new_level_usize)
579 .unwrap_or(pulldown_cmark::HeadingLevel::H6);
580 Event::End(pulldown_cmark::TagEnd::Heading(new_level))
581 }
582 _ => event,
583 });
584
585 let mut out_buf = String::with_capacity(markdown.len() + 128); cmark(transformed_events, &mut out_buf).expect("Markdown formatting failed");
587 out_buf
588}
589
590fn indent_string(s: &str, amount: usize) -> String {
592 let prefix = " ".repeat(amount);
593 s.lines()
594 .map(|line| format!("{}{}", prefix, line))
595 .collect::<Vec<_>>()
596 .join("\n")
597}
598
599fn clean_trait_path(path_str: &str) -> String {
601 path_str
602 .replace("core::marker::", "")
603 .replace("core::ops::", "") .replace("core::fmt::", "")
605 .replace("core::cmp::", "")
606 .replace("core::clone::", "")
607 .replace("core::hash::", "")
608 .replace("core::panic::unwind_safe::", "") .replace("core::", "") .replace("alloc::string::", "") .replace("alloc::vec::", "")
613 .replace("alloc::boxed::", "")
614 .replace("alloc::borrow::", "") .replace("alloc::", "") .replace("std::", "") }
618
619fn format_id_path_canonical(id: &Id, krate: &Crate) -> String {
621 krate
622 .paths
623 .get(id)
624 .map(|p| p.path.join("::"))
625 .unwrap_or_else(|| {
626 krate
628 .index
629 .get(id)
630 .and_then(|item| item.name.as_deref())
631 .map_or_else(|| format!("{{id:{}}}", id.0), |name| name.to_string())
632 })
633}
634
635fn format_path(path: &Path, krate: &Crate) -> String {
637 let base_path = format_id_path_canonical(&path.id, krate);
639
640 let cleaned_base_path = clean_trait_path(&base_path); if let Some(args) = path.args.as_ref() {
643 let args_str = format_generic_args(args, krate);
644 if !args_str.is_empty() {
645 format!("{}<{}>", cleaned_base_path, args_str) } else {
647 cleaned_base_path }
649 } else {
650 cleaned_base_path }
652}
653
654fn format_poly_trait(poly_trait: &PolyTrait, krate: &Crate) -> String {
655 let hrtb = if poly_trait.generic_params.is_empty() {
656 "".to_string()
657 } else {
658 format!(
659 "for<{}> ",
660 poly_trait
661 .generic_params
662 .iter()
663 .map(|p| format_generic_param_def(p, krate)) .collect::<Vec<_>>()
665 .join(", ")
666 )
667 };
668 format!("{}{}", hrtb, format_path(&poly_trait.trait_, krate)) }
670
671fn format_type(ty: &Type, krate: &Crate) -> String {
672 match ty {
673 Type::ResolvedPath(p) => format_path(p, krate),
674 Type::DynTrait(dt) => {
675 let lifetime_bound = dt
676 .lifetime
677 .as_ref()
678 .map(|lt| format!(" + {}", lt)) .unwrap_or_default();
680 format!(
681 "dyn {}{}",
682 dt.traits
683 .iter()
684 .map(|pt| format_poly_trait(pt, krate))
685 .collect::<Vec<_>>()
686 .join(" + "),
687 lifetime_bound
688 )
689 }
690 Type::Generic(name) => name.clone(),
691 Type::Primitive(name) => name.clone(),
692 Type::FunctionPointer(fp) => {
693 let hrtb = if fp.generic_params.is_empty() {
694 "".to_string()
695 } else {
696 format!(
697 "for<{}> ",
698 fp.generic_params
699 .iter()
700 .map(|p| format_generic_param_def(p, krate))
701 .collect::<Vec<_>>()
702 .join(", ")
703 )
704 };
705 let abi = if !matches!(fp.header.abi, Abi::Rust) {
706 format!("extern \"{:?}\" ", fp.header.abi) } else {
708 "".to_string()
709 };
710 let unsafe_kw = if fp.header.is_unsafe { "unsafe " } else { "" };
711 format!(
712 "{}{}{}fn({}){}",
713 hrtb,
714 unsafe_kw,
715 abi,
716 fp.sig
717 .inputs
718 .iter()
719 .map(|(_name, type_)| format_type(type_, krate)) .collect::<Vec<_>>()
721 .join(", "),
722 fp.sig
723 .output
724 .as_ref()
725 .map(|t| format!(" -> {}", format_type(t, krate)))
726 .unwrap_or_default()
727 )
728 }
729 Type::Tuple(types) => {
730 if types.is_empty() {
732 "()".to_string()
733 } else {
734 format!(
735 "({})",
736 types
737 .iter()
738 .map(|t| format_type(t, krate))
739 .collect::<Vec<_>>()
740 .join(", ")
741 )
742 }
743 }
744 Type::Slice(inner) => format!("[{}]", format_type(inner, krate)),
745 Type::Array { type_, len } => format!("[{}; {}]", format_type(type_, krate), len),
746 Type::Pat { type_, .. } => format!("pat {}", format_type(type_, krate)), Type::ImplTrait(bounds) => {
748 format!(
749 "impl {}",
750 bounds
751 .iter()
752 .map(|b| format_generic_bound(b, krate))
753 .collect::<Vec<_>>()
754 .join(" + ")
755 )
756 }
757 Type::Infer => "_".to_string(),
758 Type::RawPointer { is_mutable, type_ } => {
759 format!(
760 "*{}{}",
761 if *is_mutable { "mut " } else { "const " },
762 format_type(type_, krate)
763 )
764 }
765 Type::BorrowedRef {
766 lifetime,
767 is_mutable,
768 type_,
769 } => format!(
770 "&{}{}{}",
771 lifetime
772 .as_ref()
773 .map(|lt| format!("{} ", lt)) .unwrap_or_default(),
775 if *is_mutable { "mut " } else { "" },
776 format_type(type_, krate)
777 ),
778 Type::QualifiedPath {
779 name,
780 args,
781 self_type,
782 trait_,
783 } => {
784 let self_type_str = format_type(self_type, krate);
785 let trait_str = trait_
786 .as_ref()
787 .map(|t| format_path(t, krate)) .unwrap_or("_".to_string());
789 let args_str = args
791 .as_ref()
792 .map(|a| format_generic_args(a, krate))
793 .unwrap_or_default();
794
795 format!(
796 "<{} as {}>::{}{}",
797 self_type_str,
798 trait_str,
799 name,
800 if args_str.is_empty() {
801 "".to_string()
802 } else {
803 format!("<{}>", args_str)
804 }
805 )
806 }
807 }
808}
809
810fn format_generic_args(args: &GenericArgs, krate: &Crate) -> String {
811 match args {
812 GenericArgs::AngleBracketed {
813 args, constraints, ..
814 } => {
815 let arg_strs: Vec<String> = args.iter().map(|a| format_generic_arg(a, krate)).collect();
816 let constraint_strs: Vec<String> = constraints
817 .iter()
818 .map(|c| match c {
819 rustdoc_types::AssocItemConstraint {
820 name,
821 args: assoc_args,
822 binding: rustdoc_types::AssocItemConstraintKind::Equality(term),
823 } => {
824 let assoc_args_str = assoc_args
825 .as_ref()
826 .map(|a| format_generic_args(a, krate))
827 .unwrap_or_default();
828 format!(
829 "{}{}{}{} = {}",
830 name,
831 if assoc_args_str.is_empty() { "" } else { "<" },
832 assoc_args_str,
833 if assoc_args_str.is_empty() { "" } else { ">" },
834 format_term(term, krate)
835 )
836 }
837 rustdoc_types::AssocItemConstraint {
838 name,
839 args: assoc_args,
840 binding: rustdoc_types::AssocItemConstraintKind::Constraint(bounds),
841 } => {
842 let assoc_args_str = assoc_args
843 .as_ref()
844 .map(|a| format_generic_args(a, krate))
845 .unwrap_or_default();
846 format!(
847 "{}{}{}{}: {}",
848 name,
849 if assoc_args_str.is_empty() { "" } else { "<" },
850 assoc_args_str,
851 if assoc_args_str.is_empty() { "" } else { ">" },
852 bounds
853 .iter()
854 .map(|bnd| format_generic_bound(bnd, krate))
855 .collect::<Vec<_>>()
856 .join(" + ")
857 )
858 }
859 })
860 .collect();
861 let mut all_strs = arg_strs;
862 all_strs.extend(constraint_strs);
863 all_strs.join(", ")
864 }
865 GenericArgs::Parenthesized { inputs, output, .. } => {
866 format!(
867 "({}) -> {}",
868 inputs
869 .iter()
870 .map(|t| format_type(t, krate))
871 .collect::<Vec<_>>()
872 .join(", "),
873 output
874 .as_ref()
875 .map_or("()".to_string(), |t| format_type(t, krate))
876 )
877 }
878 GenericArgs::ReturnTypeNotation => String::new(),
879 }
880}
881
882fn format_const_expr(constant: &Constant) -> String {
883 if let Some(v) = &constant.value
885 && v != &constant.expr
886 {
887 return format!("{} /* = {} */", constant.expr, v);
888 }
889 constant.expr.clone()
890}
891
892fn format_discriminant_expr(discr: &Discriminant) -> String {
894 if discr.value != discr.expr {
895 format!("{} /* = {} */", discr.expr, discr.value)
896 } else {
897 discr.expr.clone()
898 }
899}
900
901fn format_generic_arg(arg: &GenericArg, krate: &Crate) -> String {
902 match arg {
903 GenericArg::Lifetime(lt) => lt.to_string(), GenericArg::Type(ty) => format_type(ty, krate),
905 GenericArg::Const(c) => format_const_expr(c),
906 GenericArg::Infer => "_".to_string(),
907 }
908}
909
910fn format_generic_bound(bound: &GenericBound, krate: &Crate) -> String {
911 match bound {
912 GenericBound::TraitBound {
913 trait_, generic_params, modifier,
916 ..
917 } => {
918 let hrtb = if generic_params.is_empty() {
919 "".to_string()
920 } else {
921 format!(
922 "for<{}> ",
923 generic_params
924 .iter()
925 .map(|p| format_generic_param_def(p, krate)) .collect::<Vec<_>>()
927 .join(", ")
928 )
929 };
930 let mod_str = match modifier {
931 rustdoc_types::TraitBoundModifier::None => "",
932 rustdoc_types::TraitBoundModifier::Maybe => "?",
933 rustdoc_types::TraitBoundModifier::MaybeConst => "?const ", };
935 format!("{}{}{}", hrtb, mod_str, format_path(trait_, krate)) }
937 GenericBound::Outlives(lifetime) => lifetime.to_string(), GenericBound::Use(args) => {
939 format!(
941 "use<{}>",
942 args.iter()
943 .map(|a| match a {
944 rustdoc_types::PreciseCapturingArg::Lifetime(lt) => format!("'{}", lt),
945 rustdoc_types::PreciseCapturingArg::Param(id_str) => id_str.clone(), })
947 .collect::<Vec<_>>()
948 .join(", ")
949 )
950 }
951 }
952}
953
954fn format_term(term: &Term, krate: &Crate) -> String {
955 match term {
956 Term::Type(t) => format_type(t, krate),
957 Term::Constant(c) => format_const_expr(c),
958 }
959}
960
961fn format_generic_param_def(p: &GenericParamDef, krate: &Crate) -> String {
962 match &p.kind {
963 rustdoc_types::GenericParamDefKind::Lifetime { .. } => p.name.to_string(), rustdoc_types::GenericParamDefKind::Type {
965 bounds,
966 default,
967 is_synthetic,
968 ..
969 } => {
970 format!(
971 "{}{}{}{}",
972 if *is_synthetic { "impl " } else { "" },
973 p.name,
974 if bounds.is_empty() {
975 "".to_string()
976 } else {
977 format!(
978 ": {}",
979 bounds
980 .iter()
981 .map(|b| format_generic_bound(b, krate))
982 .collect::<Vec<_>>()
983 .join(" + ")
984 )
985 },
986 default
987 .as_ref()
988 .map(|t| format!(" = {}", format_type(t, krate)))
989 .unwrap_or_default()
990 )
991 }
992 rustdoc_types::GenericParamDefKind::Const { type_, default, .. } => {
993 format!(
994 "const {}: {}{}",
995 p.name,
996 format_type(type_, krate),
997 default
998 .as_deref()
999 .map(|d| format!(" = {}", d))
1000 .unwrap_or_default()
1001 )
1002 }
1003 }
1004}
1005
1006fn format_generics_full(generics: &Generics, krate: &Crate) -> String {
1008 if generics.params.is_empty() && generics.where_predicates.is_empty() {
1009 return String::new();
1010 }
1011
1012 let mut s = String::new();
1013 let params_str = if !generics.params.is_empty() {
1014 format!(
1015 "<{}>",
1016 generics
1017 .params
1018 .iter()
1019 .map(|p| format_generic_param_def(p, krate))
1020 .collect::<Vec<_>>()
1021 .join(", ")
1022 )
1023 } else {
1024 String::new()
1025 };
1026
1027 let where_clause = format_generics_where_only(&generics.where_predicates, krate);
1028
1029 if !params_str.is_empty() {
1030 write!(s, "{}", params_str).unwrap();
1031 }
1032 if !where_clause.is_empty() {
1033 if !params_str.is_empty() && where_clause.contains('\n') {
1035 write!(s, "\n {}", where_clause).unwrap();
1036 } else {
1037 write!(s, " {}", where_clause).unwrap(); }
1039 }
1040
1041 s
1042}
1043
1044fn format_generics_params_only(params: &[GenericParamDef], krate: &Crate) -> String {
1046 if params.is_empty() {
1047 return String::new();
1048 }
1049 format!(
1050 "<{}>",
1051 params
1052 .iter()
1053 .map(|p| format_generic_param_def(p, krate))
1054 .collect::<Vec<_>>()
1055 .join(", ")
1056 )
1057}
1058
1059fn format_generics_where_only(predicates: &[WherePredicate], krate: &Crate) -> String {
1061 if predicates.is_empty() {
1062 return String::new();
1063 }
1064 let clauses: Vec<String> = predicates
1065 .iter()
1066 .map(|p| match p {
1067 WherePredicate::BoundPredicate {
1068 type_,
1069 bounds,
1070 generic_params,
1071 ..
1072 } => {
1073 let hrtb = if generic_params.is_empty() {
1074 "".to_string()
1075 } else {
1076 format!(
1077 "for<{}> ",
1078 generic_params
1079 .iter()
1080 .map(|gp| format_generic_param_def(gp, krate))
1081 .collect::<Vec<_>>()
1082 .join(", ")
1083 )
1084 };
1085 format!(
1086 "{}{}: {}",
1087 hrtb,
1088 format_type(type_, krate),
1089 bounds
1090 .iter()
1091 .map(|b| format_generic_bound(b, krate))
1092 .collect::<Vec<_>>()
1093 .join(" + ")
1094 )
1095 }
1096 WherePredicate::LifetimePredicate {
1097 lifetime, outlives, ..
1098 } => {
1099 format!(
1100 "{}: {}",
1101 lifetime,
1102 outlives
1103 .iter()
1104 .map(|lt| lt.to_string()) .collect::<Vec<_>>()
1106 .join(" + ")
1107 )
1108 }
1109 WherePredicate::EqPredicate { lhs, rhs, .. } => {
1110 format!("{} == {}", format_type(lhs, krate), format_term(rhs, krate))
1111 }
1112 })
1113 .collect();
1114
1115 let total_len = clauses.iter().map(|s| s.len()).sum::<usize>();
1117 let is_multiline = clauses.len() > 1 || total_len > 60; if is_multiline {
1120 format!("where\n {}", clauses.join(",\n ")) } else {
1122 format!("where {}", clauses.join(", "))
1123 }
1124}
1125
1126#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1130enum TraitImplCategory {
1131 Simple,
1133 GenericOrComplex,
1135 Auto,
1137 Blanket,
1139}
1140
1141#[derive(Debug, Clone)]
1149struct FormattedTraitImpl {
1150 trait_id: Id,
1151 trait_generics: Generics,
1154 is_unsafe_impl: bool,
1155 is_negative: bool,
1156 category: TraitImplCategory,
1158 formatted_markdown_list_entry: String,
1160 impl_id: Option<Id>,
1165}
1166
1167impl PartialEq for FormattedTraitImpl {
1168 fn eq(&self, other: &Self) -> bool {
1171 self.trait_id == other.trait_id
1172 && self.trait_generics == other.trait_generics && self.is_unsafe_impl == other.is_unsafe_impl
1174 && self.is_negative == other.is_negative
1175 && self.category == other.category }
1177}
1178impl Eq for FormattedTraitImpl {}
1179
1180impl Hash for FormattedTraitImpl {
1181 fn hash<H: Hasher>(&self, state: &mut H) {
1184 self.trait_id.hash(state);
1185 self.trait_generics.hash(state); self.is_unsafe_impl.hash(state);
1187 self.is_negative.hash(state);
1188 self.category.hash(state); }
1190}
1191
1192fn generic_args_to_generics(args_opt: Option<Box<GenericArgs>>, krate: &Crate) -> Generics {
1196 let mut params = Vec::new();
1197 let mut where_predicates = Vec::new(); if let Some(args_box) = args_opt
1200 && let GenericArgs::AngleBracketed {
1201 args, constraints, ..
1202 } = *args_box
1203 {
1204 for arg in args {
1205 match arg {
1206 GenericArg::Type(t) => {
1207 let name = match t {
1208 Type::Generic(g_name) => g_name,
1209 _ => format_type(&t, krate), };
1211 params.push(GenericParamDef {
1212 name,
1213 kind: rustdoc_types::GenericParamDefKind::Type {
1214 bounds: vec![], default: None,
1216 is_synthetic: false,
1217 },
1218 });
1219 }
1220 GenericArg::Lifetime(lt_name) => {
1221 params.push(GenericParamDef {
1222 name: lt_name,
1223 kind: rustdoc_types::GenericParamDefKind::Lifetime { outlives: vec![] },
1224 });
1225 }
1226 GenericArg::Const(c) => {
1227 params.push(GenericParamDef {
1228 name: c.expr,
1229 kind: rustdoc_types::GenericParamDefKind::Const {
1230 type_: Type::Infer, default: None,
1232 },
1233 });
1234 }
1235 GenericArg::Infer => {
1236 params.push(GenericParamDef {
1237 name: "_".to_string(),
1238 kind: rustdoc_types::GenericParamDefKind::Type {
1239 bounds: vec![],
1240 default: None,
1241 is_synthetic: true,
1242 },
1243 });
1244 }
1245 }
1246 }
1247 for constraint in constraints {
1249 match constraint {
1250 rustdoc_types::AssocItemConstraint {
1251 name: assoc_name,
1252 args: assoc_args, binding: rustdoc_types::AssocItemConstraintKind::Equality(term),
1254 } => {
1255 let lhs_type = Type::QualifiedPath {
1257 name: assoc_name,
1258 args: assoc_args.clone(),
1259 self_type: Box::new(Type::Generic("Self".to_string())), trait_: None, };
1262 where_predicates.push(WherePredicate::EqPredicate {
1263 lhs: lhs_type,
1264 rhs: term,
1265 });
1266 }
1267 rustdoc_types::AssocItemConstraint {
1268 name: assoc_name,
1269 args: assoc_args,
1270 binding: rustdoc_types::AssocItemConstraintKind::Constraint(bounds),
1271 } => {
1272 let for_type = Type::QualifiedPath {
1273 name: assoc_name,
1274 args: assoc_args.clone(),
1275 self_type: Box::new(Type::Generic("Self".to_string())),
1276 trait_: None,
1277 };
1278 where_predicates.push(WherePredicate::BoundPredicate {
1279 type_: for_type,
1280 bounds,
1281 generic_params: vec![], });
1283 }
1284 }
1285 }
1286 }
1287
1288 Generics {
1289 params,
1290 where_predicates,
1291 }
1292}
1293
1294fn get_trait_for_type_generics(item: &Item) -> Option<&Generics> {
1295 match item.inner {
1296 ItemEnum::Struct(ref s) => Some(&s.generics),
1297 ItemEnum::Enum(ref e) => Some(&e.generics),
1298 ItemEnum::Union(ref u) => Some(&u.generics),
1299 _ => None,
1300 }
1301}
1302
1303fn trait_impl_has_associated_items(imp: &Impl, krate: &Crate) -> bool {
1304 imp.items.iter().any(|id| {
1305 if let Some(item) = krate.index.get(id) {
1306 matches!(
1307 item.inner,
1308 ItemEnum::AssocType { .. } | ItemEnum::AssocConst { .. }
1309 )
1310 } else {
1311 false
1312 }
1313 })
1314}
1315
1316fn is_passthrough_generic_impl_check(imp: &Impl, trait_path: &Path, krate: &Crate) -> bool {
1321 let trait_path_is_simple = trait_path.args.as_ref().is_none_or(|ga| {
1323 matches!(ga.as_ref(), GenericArgs::AngleBracketed { args, constraints } if args.is_empty() && constraints.is_empty())
1324 });
1325 if !trait_path_is_simple {
1326 return false;
1327 }
1328
1329 if trait_impl_has_associated_items(imp, krate) {
1331 return false;
1332 }
1333
1334 let Type::ResolvedPath(for_path) = &imp.for_ else {
1336 return false;
1337 };
1338
1339 let Some(for_type_item) = krate.index.get(&for_path.id) else {
1341 return false;
1342 };
1343 let Some(for_generics) = get_trait_for_type_generics(for_type_item) else {
1344 return false;
1345 };
1346
1347 let Some(for_args_box) = &for_path.args else {
1349 return imp.generics.params.is_empty() && imp.generics.where_predicates.is_empty();
1351 };
1352 let GenericArgs::AngleBracketed { args: for_args, .. } = for_args_box.as_ref() else {
1353 return false;
1354 };
1355
1356 if for_generics.params.len() != for_args.len()
1358 || for_generics.params.len() != imp.generics.params.len()
1359 {
1360 return false;
1361 }
1362
1363 if for_generics.where_predicates != imp.generics.where_predicates {
1365 return false;
1366 }
1367
1368 let impl_params_map: HashMap<&String, &GenericParamDef> =
1371 imp.generics.params.iter().map(|p| (&p.name, p)).collect();
1372
1373 for (for_arg, for_param_def) in for_args.iter().zip(for_generics.params.iter()) {
1374 let for_arg_name = match for_arg {
1375 GenericArg::Lifetime(name) => Some(name),
1376 GenericArg::Type(Type::Generic(param_name)) => Some(param_name),
1377 _ => return false, };
1379
1380 let Some(for_arg_name_str) = for_arg_name else {
1381 return false;
1382 };
1383 let Some(impl_param_def) = impl_params_map.get(for_arg_name_str) else {
1384 return false; };
1386
1387 match (&impl_param_def.kind, &for_param_def.kind) {
1389 (
1390 rustdoc_types::GenericParamDefKind::Lifetime {
1391 outlives: impl_outlives,
1392 },
1393 rustdoc_types::GenericParamDefKind::Lifetime {
1394 outlives: for_outlives,
1395 },
1396 ) => {
1397 if impl_outlives != for_outlives {
1398 return false;
1399 }
1400 }
1401 (
1402 rustdoc_types::GenericParamDefKind::Type {
1403 bounds: impl_bounds,
1404 ..
1405 },
1406 rustdoc_types::GenericParamDefKind::Type {
1407 bounds: for_bounds, ..
1408 },
1409 ) => {
1410 if impl_bounds != for_bounds {
1411 return false;
1412 }
1413 }
1414 (
1415 rustdoc_types::GenericParamDefKind::Const { .. },
1416 rustdoc_types::GenericParamDefKind::Const { .. },
1417 ) => {
1418 }
1420 _ => return false, }
1422 }
1423
1424 true
1426}
1427
1428impl FormattedTraitImpl {
1429 fn from_impl(
1431 imp: &Impl,
1432 impl_id: Option<Id>,
1433 trait_path: &Path,
1434 krate: &Crate,
1435 printer: &Printer, ) -> Self {
1437 let trait_path_str = format_id_path_canonical(&trait_path.id, krate);
1438 let cleaned_trait_path = clean_trait_path(&trait_path_str);
1439
1440 let display_path_with_generics = format!(
1441 "{}{}{}",
1442 if imp.is_negative {
1443 "!"
1444 } else {
1445 Default::default()
1446 },
1447 cleaned_trait_path,
1448 if let Some(args) = &trait_path.args {
1449 let args_str = format_generic_args(args, krate);
1450 if !args_str.is_empty() {
1451 format!("<{}>", args_str)
1452 } else {
1453 String::new()
1454 }
1455 } else {
1456 String::new()
1457 }
1458 );
1459
1460 let is_passthrough_generic_impl = is_passthrough_generic_impl_check(imp, trait_path, krate);
1461
1462 let category = if imp.is_synthetic {
1463 TraitImplCategory::Auto
1464 } else if imp.blanket_impl.is_some() {
1465 TraitImplCategory::Blanket
1466 } else if is_passthrough_generic_impl && !trait_impl_has_associated_items(imp, krate) {
1467 TraitImplCategory::Simple
1468 } else {
1469 TraitImplCategory::GenericOrComplex
1470 };
1471
1472 let mut list_entry = String::new();
1473 match category {
1474 TraitImplCategory::Simple | TraitImplCategory::Auto => {
1475 write!(list_entry, "- `{}`", display_path_with_generics).unwrap();
1476 }
1477 TraitImplCategory::GenericOrComplex => {
1478 if let Some(impl_block_str) = generate_impl_trait_block(imp, printer.krate) {
1479 if !impl_block_str.trim_end_matches("{\n}").trim().is_empty() {
1480 writeln!(list_entry, "- `{}`", display_path_with_generics).unwrap();
1481 writeln!(list_entry).unwrap();
1482 let full_code_block = format!("```rust\n{}\n```", impl_block_str);
1483 let indented_block = indent_string(&full_code_block, 4);
1484 writeln!(list_entry, "{}", indented_block).unwrap(); } else {
1486 write!(list_entry, "- `{}`", display_path_with_generics).unwrap();
1487 }
1488 } else {
1489 write!(list_entry, "- `{}`", display_path_with_generics).unwrap();
1490 }
1491 }
1492 TraitImplCategory::Blanket => {
1493 let where_clause =
1494 format_generics_where_only(&imp.generics.where_predicates, krate);
1495 if !where_clause.is_empty() {
1496 if where_clause.lines().count() == 1 {
1497 write!(
1498 list_entry,
1499 "- `{}` (`{}`)",
1500 display_path_with_generics, where_clause,
1501 )
1502 .unwrap();
1503 } else {
1504 writeln!(list_entry, "- `{}`", display_path_with_generics).unwrap();
1505 let code_block = format!("```rust\n{}\n```", where_clause);
1506 let indented_block = indent_string(&code_block, 4);
1507 write!(list_entry, "\n{}\n", indented_block).unwrap(); }
1509 } else {
1510 write!(list_entry, "- `{}`", display_path_with_generics).unwrap();
1511 }
1512 }
1513 }
1514
1515 FormattedTraitImpl {
1516 trait_id: trait_path.id,
1517 trait_generics: generic_args_to_generics(trait_path.args.clone(), krate),
1518 is_unsafe_impl: imp.is_unsafe,
1519 is_negative: imp.is_negative,
1520 category,
1521 formatted_markdown_list_entry: list_entry.trim_end().to_string(), impl_id,
1523 }
1524 }
1525
1526 fn get_impl_data<'krate_borrow>(
1528 &self,
1529 krate: &'krate_borrow Crate,
1530 ) -> Option<(&'krate_borrow Impl, Id)> {
1531 self.impl_id
1532 .and_then(|id| krate.index.get(&id))
1533 .and_then(|item| match &item.inner {
1534 ItemEnum::Impl(imp_data) => Some((imp_data, item.id)),
1535 _ => None,
1536 })
1537 }
1538
1539 #[allow(dead_code)] fn has_associated_items(&self, krate: &Crate) -> bool {
1542 if let Some(trait_item) = krate.index.get(&self.trait_id)
1543 && let ItemEnum::Trait(trait_data) = &trait_item.inner
1544 {
1545 return trait_data.items.iter().any(|assoc_id| {
1546 if let Some(assoc_item_details) = krate.index.get(assoc_id) {
1547 matches!(
1548 assoc_item_details.inner,
1549 ItemEnum::AssocType { .. } | ItemEnum::AssocConst { .. }
1550 )
1551 } else {
1552 false
1553 }
1554 });
1555 }
1556 false
1557 }
1558}
1559
1560fn generate_item_declaration(item: &Item, krate: &Crate, current_module_path: &[String]) -> String {
1564 let name = item.name.as_deref().unwrap_or(match &item.inner {
1565 ItemEnum::StructField(_) => "{unnamed_field}", _ => "{unnamed}",
1567 });
1568 match &item.inner {
1569 ItemEnum::Struct(s) => {
1570 let mut fq_path_parts = current_module_path.to_vec();
1571 if !name.is_empty() {
1572 fq_path_parts.push(name.to_string());
1573 }
1574 let fq_path = fq_path_parts.join("::");
1575 format!(
1576 "struct {}{}",
1577 fq_path,
1578 format_generics_params_only(&s.generics.params, krate)
1579 )
1580 }
1581 ItemEnum::Enum(e) => {
1582 let mut fq_path_parts = current_module_path.to_vec();
1583 if !name.is_empty() {
1584 fq_path_parts.push(name.to_string());
1585 }
1586 let fq_path = fq_path_parts.join("::");
1587 format!(
1588 "enum {}{}",
1589 fq_path,
1590 format_generics_params_only(&e.generics.params, krate)
1591 )
1592 }
1593 ItemEnum::Union(u) => {
1594 let mut fq_path_parts = current_module_path.to_vec();
1595 if !name.is_empty() {
1596 fq_path_parts.push(name.to_string());
1597 }
1598 let fq_path = fq_path_parts.join("::");
1599 format!(
1600 "union {}{}",
1601 fq_path,
1602 format_generics_params_only(&u.generics.params, krate)
1603 )
1604 }
1605 ItemEnum::Trait(t) => {
1606 let unsafe_kw = if t.is_unsafe { "unsafe " } else { "" };
1607 let auto = if t.is_auto { "auto " } else { "" };
1608 let mut fq_path_parts = current_module_path.to_vec();
1609 if !name.is_empty() {
1610 fq_path_parts.push(name.to_string());
1611 }
1612 let fq_path = fq_path_parts.join("::");
1613
1614 format!(
1615 "{}{}{}{}{}",
1616 auto,
1617 unsafe_kw,
1618 "trait ",
1619 fq_path, format_generics_params_only(&t.generics.params, krate)
1621 )
1622 }
1623 ItemEnum::Function(f) => {
1624 let mut code = String::new();
1626 write!(code, "{}", format_attributes_inline(&item.attrs)).unwrap(); write!(code, "fn {}", name).unwrap();
1628 write!(
1630 code,
1631 "{}",
1632 format_generics_params_only(&f.generics.params, krate)
1633 )
1634 .unwrap();
1635 write!(code, "(").unwrap();
1636 let args_str = f
1637 .sig
1638 .inputs
1639 .iter()
1640 .map(|(n, t)| format!("{}: {}", n, format_type(t, krate))) .collect::<Vec<_>>()
1642 .join(", ");
1643 write!(code, "{}", args_str).unwrap();
1644 if f.sig.is_c_variadic {
1645 write!(code, ", ...").unwrap();
1646 }
1647 write!(code, ")").unwrap();
1648 if let Some(output_type) = &f.sig.output {
1649 write!(code, " -> {}", format_type(output_type, krate)).unwrap();
1650 }
1651 code
1652 }
1653 ItemEnum::TypeAlias(ta) => format!(
1654 "type {}{}",
1655 name,
1656 format_generics_params_only(&ta.generics.params, krate)
1657 ),
1658 ItemEnum::TraitAlias(ta) => format!(
1659 "trait {}{}",
1660 name,
1661 format_generics_params_only(&ta.generics.params, krate)
1662 ),
1663 ItemEnum::Constant { .. } => format!("const {}", name), ItemEnum::Static(s) => format!("static {}{}", if s.is_mutable { "mut " } else { "" }, name),
1665 ItemEnum::Macro(_) => format!("macro {}!", name),
1666 ItemEnum::ProcMacro(pm) => {
1667 let kind_str = match pm.kind {
1668 rustdoc_types::MacroKind::Bang => "!",
1669 rustdoc_types::MacroKind::Attr => "#[]",
1670 rustdoc_types::MacroKind::Derive => "#[derive]",
1671 };
1672 format!("proc_macro {}{}", name, kind_str)
1673 }
1674 ItemEnum::Primitive(_) => format!("primitive {}", name),
1675 ItemEnum::Module(_) => format!("mod {}", name), ItemEnum::ExternCrate {
1677 name: crate_name, ..
1678 } => format!("extern crate {}", crate_name),
1679 ItemEnum::Use(_) => format!("use {}", name), ItemEnum::ExternType => format!("extern type {}", name),
1681 ItemEnum::Variant(v) => format_variant_signature(item, v, krate), ItemEnum::StructField(_) => name.to_string(), ItemEnum::AssocConst { .. } => format!("const {}", name),
1684 ItemEnum::AssocType { .. } => format!("type {}", name),
1685 ItemEnum::Impl(_) => "impl".to_string(), }
1687}
1688
1689fn generate_struct_code_block(item: &Item, s: &Struct, krate: &Crate) -> String {
1691 let name = item
1692 .name
1693 .as_deref()
1694 .expect("Struct item should have a name");
1695 let mut code = String::new();
1696 write!(
1697 code,
1698 "{}pub struct {}",
1699 format_attributes(&item.attrs), name
1701 )
1702 .unwrap();
1703 let generics_str = format_generics_full(&s.generics, krate);
1705 let where_is_multiline = generics_str.contains("where\n");
1706 write!(code, "{}", generics_str).unwrap();
1707
1708 match &s.kind {
1709 StructKind::Plain { fields, .. } => {
1710 if where_is_multiline {
1712 write!(code, " {{").unwrap(); } else {
1714 write!(code, " {{").unwrap(); }
1716
1717 if !fields.is_empty() {
1718 writeln!(code).unwrap();
1719 }
1720 for field_id in fields {
1721 if let Some(field_item) = krate.index.get(field_id)
1722 && let ItemEnum::StructField(field_type) = &field_item.inner
1723 {
1724 let field_name = field_item.name.as_deref().unwrap_or("_");
1725 writeln!(
1726 code,
1727 " {}pub {}: {},",
1728 format_attributes_inline(&field_item.attrs), field_name,
1730 format_type(field_type, krate)
1731 )
1732 .unwrap();
1733 }
1734 }
1735 if !fields.is_empty() && !code.ends_with('\n') {
1736 writeln!(code).unwrap();
1737 }
1738 write!(code, "}}").unwrap();
1739 }
1740 StructKind::Tuple(fields) => {
1741 write!(code, "(").unwrap();
1743 let field_types: Vec<String> = fields
1744 .iter()
1745 .filter_map(|opt_id| {
1746 opt_id
1747 .as_ref()
1748 .and_then(|id| krate.index.get(id))
1749 .and_then(|field_item| {
1750 if let ItemEnum::StructField(field_type) = &field_item.inner {
1751 Some(format!(
1752 "{}pub {}",
1753 format_attributes_inline(&field_item.attrs), format_type(field_type, krate)
1755 ))
1756 } else {
1757 None
1758 }
1759 })
1760 })
1761 .collect();
1762 write!(code, "{}", field_types.join(", ")).unwrap();
1763 write!(code, ")").unwrap();
1764 if !where_is_multiline {
1766 write!(code, ";").unwrap();
1767 }
1768 }
1769 StructKind::Unit => {
1770 if !where_is_multiline {
1772 write!(code, ";").unwrap();
1773 }
1774 }
1775 }
1776 code
1777}
1778
1779fn generate_enum_code_block(item: &Item, e: &Enum, krate: &Crate) -> String {
1781 let name = item.name.as_deref().expect("Enum item should have a name");
1782 let mut code = String::new();
1783 write!(
1784 code,
1785 "{}pub enum {}",
1786 format_attributes(&item.attrs), name
1788 )
1789 .unwrap();
1790 let generics_str = format_generics_full(&e.generics, krate);
1791 write!(code, "{}", generics_str).unwrap();
1792 write!(code, " {{").unwrap();
1793
1794 if !e.variants.is_empty() {
1795 writeln!(code).unwrap();
1796 }
1797 for variant_id in &e.variants {
1798 if let Some(variant_item) = krate.index.get(variant_id)
1799 && let ItemEnum::Variant(variant_data) = &variant_item.inner
1800 {
1801 write!(
1802 code,
1803 " {}",
1804 format_variant_definition(variant_item, variant_data, krate) )
1806 .unwrap();
1807 if let Some(discr) = &variant_data.discriminant {
1809 write!(code, " = {}", format_discriminant_expr(discr)).unwrap();
1811 }
1812 writeln!(code, ",").unwrap();
1813 }
1814 }
1815 if !e.variants.is_empty() && !code.ends_with('\n') {
1816 writeln!(code).unwrap();
1817 }
1818 write!(code, "}}").unwrap();
1819 code
1820}
1821
1822fn generate_union_code_block(item: &Item, u: &Union, krate: &Crate) -> String {
1824 let name = item.name.as_deref().expect("Union item should have a name");
1825 let mut code = String::new();
1826 write!(
1827 code,
1828 "{}pub union {}",
1829 format_attributes(&item.attrs), name
1831 )
1832 .unwrap();
1833 let generics_str = format_generics_full(&u.generics, krate);
1834 write!(code, "{}", generics_str).unwrap();
1835 write!(code, " {{").unwrap();
1836
1837 if !u.fields.is_empty() {
1838 writeln!(code).unwrap();
1839 }
1840 for field_id in &u.fields {
1841 if let Some(field_item) = krate.index.get(field_id)
1842 && let ItemEnum::StructField(field_type) = &field_item.inner
1843 {
1844 let field_name = field_item.name.as_deref().unwrap_or("_");
1845 writeln!(
1846 code,
1847 " {}pub {}: {},",
1848 format_attributes_inline(&field_item.attrs), field_name,
1850 format_type(field_type, krate)
1851 )
1852 .unwrap();
1853 }
1854 }
1855 if !u.fields.is_empty() && !code.ends_with('\n') {
1856 writeln!(code).unwrap();
1857 }
1858 write!(code, "}}").unwrap();
1859 code
1860}
1861
1862fn generate_trait_code_block(item: &Item, t: &Trait, krate: &Crate) -> String {
1864 let name = item.name.as_deref().expect("Trait item should have a name");
1865 let mut code = String::new();
1866
1867 write!(code, "{}", format_attributes(&item.attrs)).unwrap(); if t.is_auto {
1870 write!(code, "pub auto ").unwrap();
1871 }
1872 if t.is_unsafe {
1873 write!(code, "pub unsafe ").unwrap();
1874 } else if !t.is_auto {
1875 write!(code, "pub ").unwrap();
1877 }
1878 write!(code, "trait {}", name).unwrap();
1879 write!(
1881 code,
1882 "{}",
1883 format_generics_params_only(&t.generics.params, krate)
1884 )
1885 .unwrap();
1886 if !t.bounds.is_empty() {
1887 write!(
1888 code,
1889 ": {}",
1890 t.bounds
1891 .iter()
1892 .map(|b| format_generic_bound(b, krate))
1893 .collect::<Vec<_>>()
1894 .join(" + ")
1895 )
1896 .unwrap();
1897 }
1898 let where_clause = format_generics_where_only(&t.generics.where_predicates, krate);
1900 if !where_clause.is_empty() {
1901 if where_clause.contains('\n') {
1902 write!(code, "\n {}", where_clause).unwrap(); } else {
1904 write!(code, " {}", where_clause).unwrap(); }
1906 }
1907
1908 if t.items.is_empty() {
1910 write!(code, " {{}}").unwrap();
1911 } else {
1912 if where_clause.contains('\n') {
1913 write!(code, " {{").unwrap(); } else {
1915 write!(code, " {{").unwrap(); }
1917 writeln!(code).unwrap();
1918
1919 for item_id in &t.items {
1921 if let Some(assoc_item) = krate.index.get(item_id) {
1922 match &assoc_item.inner {
1923 ItemEnum::AssocConst { type_, value, .. } => {
1924 write!(
1925 code,
1926 " {}const {}: {}",
1927 format_attributes_inline(&assoc_item.attrs), assoc_item.name.as_deref().unwrap_or("_"),
1929 format_type(type_, krate)
1930 )
1931 .unwrap();
1932 if let Some(val) = value {
1933 write!(code, " = {};", val).unwrap(); } else {
1935 write!(code, ";").unwrap();
1936 }
1937 writeln!(code).unwrap();
1938 }
1939 ItemEnum::AssocType { bounds, type_, .. } => {
1940 write!(
1941 code,
1942 " {}type {}",
1943 format_attributes_inline(&assoc_item.attrs), assoc_item.name.as_deref().unwrap_or("_")
1945 )
1946 .unwrap();
1947 if !bounds.is_empty() {
1948 write!(
1949 code,
1950 ": {}",
1951 bounds
1952 .iter()
1953 .map(|b| format_generic_bound(b, krate))
1954 .collect::<Vec<_>>()
1955 .join(" + ")
1956 )
1957 .unwrap();
1958 }
1959 if let Some(ty) = type_ {
1960 write!(code, " = {};", format_type(ty, krate)).unwrap();
1961 } else {
1962 write!(code, ";").unwrap();
1963 }
1964 writeln!(code).unwrap();
1965 }
1966 ItemEnum::Function(f) => {
1967 writeln!(
1969 code,
1970 " {};",
1971 generate_function_code_block(assoc_item, f, krate)
1972 )
1973 .unwrap();
1974 }
1975 _ => {} }
1977 }
1978 }
1979 if !code.ends_with('\n') {
1980 writeln!(code).unwrap();
1981 }
1982 write!(code, "}}").unwrap();
1983 }
1984 code
1985}
1986
1987fn format_impl_decl(imp: &Impl, krate: &Crate) -> String {
1989 let mut decl = String::new();
1990 if imp.is_unsafe {
1991 write!(decl, "unsafe ").unwrap();
1992 }
1993 write!(decl, "impl").unwrap();
1994
1995 let generics_params = format_generics_params_only(&imp.generics.params, krate);
1997 if !generics_params.is_empty() {
1998 write!(decl, "{}", generics_params).unwrap();
1999 }
2000
2001 if let Some(trait_path) = &imp.trait_ {
2003 write!(decl, " {} for", format_path(trait_path, krate)).unwrap();
2004 }
2005 write!(decl, " {}", format_type(&imp.for_, krate)).unwrap();
2006
2007 let where_clause = format_generics_where_only(&imp.generics.where_predicates, krate);
2009 if !where_clause.is_empty() {
2010 if where_clause.contains('\n') {
2011 write!(decl, "\n {}", where_clause).unwrap(); } else {
2013 write!(decl, " {}", where_clause).unwrap(); }
2015 }
2016 decl
2017}
2018
2019fn format_impl_decl_header_only(imp: &Impl, krate: &Crate) -> String {
2021 let mut decl = String::new();
2022 if imp.is_unsafe {
2023 write!(decl, "unsafe ").unwrap();
2024 }
2025 write!(decl, "impl").unwrap();
2026
2027 let generics_params = format_generics_params_only(&imp.generics.params, krate);
2029 if !generics_params.is_empty() {
2030 write!(decl, "{}", generics_params).unwrap();
2031 }
2032
2033 if let Some(trait_path) = &imp.trait_ {
2035 write!(decl, " {} for", format_path(trait_path, krate)).unwrap();
2037 }
2038
2039 write!(decl, " {}", format_type(&imp.for_, krate)).unwrap();
2041
2042 decl
2044}
2045
2046fn generate_impl_trait_block(imp: &Impl, krate: &Crate) -> Option<String> {
2050 let mut code = String::new();
2051 let impl_header = format_impl_decl(imp, krate);
2052 writeln!(code, "{} {{", impl_header).unwrap();
2053
2054 let mut assoc_items_content = String::new();
2055 let mut has_printable_assoc_items = false;
2056
2057 for assoc_item_id in &imp.items {
2060 if let Some(assoc_item) = krate.index.get(assoc_item_id) {
2061 match &assoc_item.inner {
2062 ItemEnum::AssocConst { type_, value, .. } => {
2063 has_printable_assoc_items = true;
2064 write!(
2065 assoc_items_content,
2066 " {}const {}: {}",
2067 format_attributes_inline(&assoc_item.attrs), assoc_item.name.as_deref().unwrap_or("_"),
2069 format_type(type_, krate)
2070 )
2071 .unwrap();
2072 if let Some(val) = value {
2073 write!(assoc_items_content, " = {};", val).unwrap();
2074 } else {
2075 write!(assoc_items_content, ";").unwrap();
2076 }
2077 writeln!(assoc_items_content).unwrap();
2078 }
2079 ItemEnum::AssocType { bounds, type_, .. } => {
2080 has_printable_assoc_items = true;
2081 write!(
2082 assoc_items_content,
2083 " {}type {}",
2084 format_attributes_inline(&assoc_item.attrs), assoc_item.name.as_deref().unwrap_or("_")
2086 )
2087 .unwrap();
2088 if !bounds.is_empty() {
2089 let bounds_str = bounds
2090 .iter()
2091 .map(|b| format_generic_bound(b, krate))
2092 .collect::<Vec<_>>()
2093 .join(" + ");
2094 write!(assoc_items_content, ": {}", bounds_str).unwrap();
2095 }
2096 if let Some(ty) = type_ {
2097 write!(assoc_items_content, " = {}", format_type(ty, krate)).unwrap();
2098 }
2099 write!(assoc_items_content, ";").unwrap();
2100 writeln!(assoc_items_content).unwrap();
2101 }
2102 _ => {}
2103 }
2104 }
2105 }
2106
2107 if has_printable_assoc_items {
2108 if impl_header.contains('\n') && !assoc_items_content.starts_with('\n') {
2109 writeln!(code).unwrap();
2110 }
2111 write!(code, "{}", assoc_items_content).unwrap();
2112 if !code.ends_with('\n') && !assoc_items_content.is_empty() {
2113 writeln!(code).unwrap();
2114 }
2115 } else if impl_header.contains('\n') {
2116 writeln!(code).unwrap();
2117 }
2118
2119 write!(code, "}}").unwrap();
2120 if !has_printable_assoc_items {
2121 return None;
2122 }
2123 Some(code)
2124}
2125
2126fn generate_function_code_block(item: &Item, f: &Function, krate: &Crate) -> String {
2128 let name = item.name.as_deref().expect("Function should have a name");
2129 let mut code = String::new();
2130
2131 write!(code, "{}", format_attributes_inline(&item.attrs)).unwrap(); write!(code, "pub ").unwrap();
2134 if f.header.is_const {
2135 write!(code, "const ").unwrap();
2136 }
2137 if f.header.is_async {
2138 write!(code, "async ").unwrap();
2139 }
2140 if f.header.is_unsafe {
2141 write!(code, "unsafe ").unwrap();
2142 }
2143 if !matches!(f.header.abi, Abi::Rust) {
2144 write!(code, "extern \"{:?}\" ", f.header.abi).unwrap(); }
2146
2147 write!(code, "fn {}", name).unwrap();
2149 let generics_str = format_generics_full(&f.generics, krate);
2151 let where_is_multiline = generics_str.contains("where\n");
2152 write!(code, "{}", generics_str).unwrap();
2153
2154 write!(code, "(").unwrap();
2156 let args_str = f
2157 .sig
2158 .inputs
2159 .iter()
2160 .map(|(n, t)| format!("{}: {}", n, format_type(t, krate))) .collect::<Vec<_>>()
2162 .join(", ");
2163 write!(code, "{}", args_str).unwrap();
2164 if f.sig.is_c_variadic {
2165 write!(code, ", ...").unwrap();
2167 }
2168 write!(code, ")").unwrap();
2169
2170 if let Some(output_type) = &f.sig.output {
2172 write!(code, " -> {}", format_type(output_type, krate)).unwrap();
2173 }
2174
2175 if f.has_body {
2177 if where_is_multiline {
2178 write!(code, " {{ ... }}").unwrap(); } else {
2180 write!(code, " {{ ... }}").unwrap(); }
2182 } else if !where_is_multiline {
2183 write!(code, ";").unwrap();
2185 }
2186
2187 code
2188}
2189
2190fn format_variant_definition(item: &Item, v: &Variant, krate: &Crate) -> String {
2192 let name = item.name.as_deref().unwrap_or("{Unnamed}");
2193 let attrs_str = format_attributes_inline(&item.attrs); match &v.kind {
2195 VariantKind::Plain => format!("{}{}", attrs_str, name),
2196 VariantKind::Tuple(fields) => {
2197 let types: Vec<String> = fields
2199 .iter()
2200 .filter_map(|opt_id| {
2201 opt_id
2202 .as_ref()
2203 .and_then(|id| krate.index.get(id))
2204 .and_then(|field_item| {
2205 if let ItemEnum::StructField(ty) = &field_item.inner {
2206 Some(format!(
2207 "{}{}", format_attributes_inline(&field_item.attrs), format_type(ty, krate)
2210 ))
2211 } else {
2212 None
2213 }
2214 })
2215 })
2216 .collect();
2217 format!("{}{}({})", attrs_str, name, types.join(", "))
2218 }
2219 VariantKind::Struct { fields, .. } => {
2220 let fields_str: Vec<String> = fields
2222 .iter()
2223 .filter_map(|id| {
2224 krate.index.get(id).and_then(|field_item| {
2225 if let ItemEnum::StructField(ty) = &field_item.inner {
2226 let field_name = field_item.name.as_deref().unwrap_or("_");
2227 Some(format!(
2228 "{}{}: {}", format_attributes_inline(&field_item.attrs), field_name,
2231 format_type(ty, krate)
2232 ))
2233 } else {
2234 None
2235 }
2236 })
2237 })
2238 .collect();
2239 format!("{}{}{{ {} }}", attrs_str, name, fields_str.join(", "))
2240 }
2241 }
2242}
2243
2244fn format_variant_signature(item: &Item, v: &Variant, krate: &Crate) -> String {
2246 let name = item.name.as_deref().unwrap_or("{Unnamed}");
2249 let mut sig = match &v.kind {
2250 VariantKind::Plain => name.to_string(),
2251 VariantKind::Tuple(fields) => {
2252 let types: Vec<String> = fields
2253 .iter()
2254 .filter_map(|opt_id| {
2255 opt_id
2256 .as_ref()
2257 .and_then(|id| krate.index.get(id))
2258 .and_then(|field_item| {
2259 if let ItemEnum::StructField(ty) = &field_item.inner {
2260 Some(format_type(ty, krate)) } else {
2262 None
2263 }
2264 })
2265 })
2266 .collect();
2267 format!("{}({})", name, types.join(", "))
2268 }
2269 VariantKind::Struct { fields, .. } => {
2270 let fields_str: Vec<String> = fields
2271 .iter()
2272 .filter_map(|id| {
2273 krate.index.get(id).and_then(|field_item| {
2274 if let ItemEnum::StructField(ty) = &field_item.inner {
2275 let field_name = field_item.name.as_deref().unwrap_or("_");
2276 Some(format!("{}: {}", field_name, format_type(ty, krate)))
2277 } else {
2279 None
2280 }
2281 })
2282 })
2283 .collect();
2284 format!("{} {{ {} }}", name, fields_str.join(", "))
2285 }
2286 };
2287
2288 if let Some(discr) = &v.discriminant {
2289 write!(sig, " = {}", format_discriminant_expr(discr)).unwrap();
2291 }
2292 sig
2293}
2294
2295#[derive(Debug, Default, Clone)] struct ModuleTree {
2298 children: HashMap<Id, Vec<Id>>,
2300 all_modules: HashSet<Id>,
2302 top_level_modules: Vec<Id>,
2304}
2305
2306pub struct Printer<'a> {
2334 krate: &'a Crate,
2335 manifest_data: CrateManifestData,
2336 paths: Vec<String>,
2338 crate_extra: Option<CrateExtra>,
2339 include_other: bool,
2340 template_mode: bool,
2341 no_common_traits: bool,
2342 selected_ids: HashSet<Id>,
2344 resolved_modules: HashMap<Id, ResolvedModule>,
2345 graph: IdGraph,
2346 printed_ids: HashMap<Id, String>, output: String,
2348 module_tree: ModuleTree,
2349 doc_path: Vec<usize>,
2350 current_module_path: Vec<String>,
2351 crate_common_traits: HashSet<FormattedTraitImpl>,
2352 all_type_ids_with_impls: HashSet<Id>,
2353 module_common_traits: HashMap<Id, HashSet<FormattedTraitImpl>>,
2354}
2355
2356impl<'a> Printer<'a> {
2357 pub fn new(manifest: &'a CargoManifest, krate: &'a Crate) -> Self {
2364 Printer {
2365 krate,
2366 manifest_data: CrateManifestData::from_cargo_manifest(manifest),
2367 paths: Vec::new(),
2368 crate_extra: None,
2369 include_other: false,
2370 template_mode: false,
2371 no_common_traits: false,
2372 selected_ids: HashSet::new(), resolved_modules: HashMap::new(), graph: IdGraph::default(), printed_ids: HashMap::new(), output: String::new(),
2377 module_tree: Self::build_module_tree(krate), doc_path: Vec::new(),
2379 current_module_path: vec![],
2380 crate_common_traits: HashSet::new(), all_type_ids_with_impls: HashSet::new(), module_common_traits: HashMap::new(), }
2384 }
2385
2386 pub fn paths(mut self, paths: &[String]) -> Self {
2403 self.paths = paths.to_vec();
2404 self
2405 }
2406
2407 pub fn crate_extra(mut self, extra: CrateExtra) -> Self {
2411 self.crate_extra = Some(extra);
2412 self
2413 }
2414
2415 pub fn include_other(mut self) -> Self {
2422 self.include_other = true;
2423 self
2424 }
2425
2426 pub fn template_mode(mut self) -> Self {
2435 self.template_mode = true;
2436 self
2437 }
2438
2439 pub fn no_common_traits(mut self) -> Self {
2449 self.no_common_traits = true;
2450 self
2451 }
2452
2453 pub fn print(mut self) -> Result<String> {
2470 self.resolved_modules = graph::build_resolved_module_index(self.krate);
2471 let (selected_ids, graph) =
2472 graph::select_items(self.krate, &self.paths, &self.resolved_modules)?;
2473 self.selected_ids = selected_ids;
2474 self.graph = graph;
2475
2476 info!(
2477 "Generating documentation for {} selected items.",
2478 self.selected_ids.len()
2479 );
2480 if self.selected_ids.is_empty()
2481 && self
2482 .crate_extra
2483 .as_ref()
2484 .is_none_or(|ce| ce.examples.is_empty())
2485 {
2486 return Ok("No items selected for documentation and no examples found.".to_string());
2487 }
2488
2489 let (crate_common_traits, all_type_ids_with_impls) = Self::calculate_crate_common_traits(
2490 self.krate,
2491 &self.selected_ids, self.no_common_traits,
2493 &self, );
2495 self.crate_common_traits = crate_common_traits;
2496 self.all_type_ids_with_impls = all_type_ids_with_impls;
2497
2498 Ok(self.finalize())
2500 }
2501
2502 fn calculate_crate_common_traits(
2504 krate: &Crate,
2505 selected_ids: &HashSet<Id>,
2506 no_common_traits: bool,
2507 printer: &Printer,
2508 ) -> (HashSet<FormattedTraitImpl>, HashSet<Id>) {
2509 let mut all_type_ids_with_impls = HashSet::new();
2510 if no_common_traits {
2511 for item in krate.index.values() {
2512 if let ItemEnum::Impl(imp) = &item.inner
2513 && let Some(for_type_id) = get_type_id(&imp.for_)
2514 && selected_ids.contains(&for_type_id)
2515 {
2516 all_type_ids_with_impls.insert(for_type_id);
2517 }
2518 }
2519 return (HashSet::new(), all_type_ids_with_impls);
2520 }
2521
2522 let mut trait_format_counts: HashMap<Id, HashMap<FormattedTraitImpl, usize>> =
2524 HashMap::new();
2525
2526 for item in krate.index.values() {
2527 if let ItemEnum::Impl(imp) = &item.inner
2528 && let Some(for_type_id) = get_type_id(&imp.for_)
2529 && selected_ids.contains(&for_type_id)
2530 {
2531 all_type_ids_with_impls.insert(for_type_id);
2532 if let Some(trait_path) = &imp.trait_ {
2533 let norm_impl =
2534 FormattedTraitImpl::from_impl(imp, None, trait_path, krate, printer);
2535 *trait_format_counts
2536 .entry(trait_path.id)
2537 .or_default()
2538 .entry(norm_impl)
2539 .or_insert(0) += 1;
2540 }
2541 }
2542 }
2543 debug!(
2544 "Found {} types with trait implementations for crate-level common trait calculation.",
2545 all_type_ids_with_impls.len()
2546 );
2547
2548 if all_type_ids_with_impls.len() < 2 {
2549 debug!("Too few trait implementations, skipping crate 'Common Traits'");
2550 return (HashSet::new(), all_type_ids_with_impls);
2551 }
2552
2553 let mut common_traits_set = HashSet::new();
2554 if all_type_ids_with_impls.is_empty() {
2555 return (common_traits_set, all_type_ids_with_impls);
2556 }
2557
2558 let type_count_threshold = (all_type_ids_with_impls.len() as f32 * 0.5).ceil() as usize;
2559 debug!(
2560 "Crate common trait threshold (types implementing): {} (out of {} types)",
2561 type_count_threshold,
2562 all_type_ids_with_impls.len()
2563 );
2564
2565 for (trait_id, format_map) in trait_format_counts {
2566 let total_implementations_for_trait = format_map.values().sum::<usize>();
2567
2568 if total_implementations_for_trait < type_count_threshold {
2569 trace!(
2570 "Trait ID {:?} not common enough ({} implementations, need {})",
2571 trait_id, total_implementations_for_trait, type_count_threshold
2572 );
2573 continue;
2574 }
2575
2576 let mut has_positive = false;
2578 let mut has_negative = false;
2579 for f_impl in format_map.keys() {
2580 if f_impl.is_negative {
2581 has_negative = true;
2582 } else {
2583 has_positive = true;
2584 }
2585 }
2586 if has_positive && has_negative {
2587 warn!(
2588 "Trait ID {:?} has mixed positive and negative implementations, cannot be common.",
2589 trait_id
2590 );
2591 continue;
2592 }
2593
2594 if format_map.len() == 1 {
2595 if let Some(formatted_impl) = format_map.keys().next() {
2597 common_traits_set.insert(formatted_impl.clone());
2598 debug!(
2599 "Identified crate-common trait (single format): {:?} for trait ID {:?}",
2600 formatted_impl.formatted_markdown_list_entry, trait_id
2601 );
2602 }
2603 } else {
2604 let simple_format_count = format_map
2606 .iter()
2607 .find(|(f_impl, _)| f_impl.category == TraitImplCategory::Simple)
2608 .map_or(0, |(_, count)| *count);
2609
2610 if simple_format_count * 2 > total_implementations_for_trait {
2611 if let Some(simple_impl) = format_map
2613 .keys()
2614 .find(|f_impl| f_impl.category == TraitImplCategory::Simple)
2615 {
2616 common_traits_set.insert(simple_impl.clone());
2617 debug!(
2618 "Identified crate-common trait (simple format predominant): {:?} for trait ID {:?}",
2619 simple_impl.formatted_markdown_list_entry, trait_id
2620 );
2621 }
2622 } else {
2623 trace!(
2624 "Trait ID {:?} has multiple formats, but Simple is not predominant ({} of {}).",
2625 trait_id, simple_format_count, total_implementations_for_trait
2626 );
2627 }
2628 }
2629 }
2630 (common_traits_set, all_type_ids_with_impls)
2631 }
2632
2633 fn calculate_module_common_traits(&self, module_id: &Id) -> HashSet<FormattedTraitImpl> {
2635 if self.no_common_traits {
2636 return HashSet::new();
2637 }
2638
2639 let mut module_common_traits = self.crate_common_traits.clone();
2640 let mut module_types_considered = HashSet::new();
2641
2642 if let Some(resolved_mod) = self.resolved_modules.get(module_id) {
2643 for item_id_in_mod in &resolved_mod.items {
2644 if let Some(item) = self.krate.index.get(item_id_in_mod)
2645 && matches!(
2646 item.inner,
2647 ItemEnum::Struct(_)
2648 | ItemEnum::Enum(_)
2649 | ItemEnum::Union(_)
2650 | ItemEnum::Primitive(_)
2651 )
2652 && self.selected_ids.contains(item_id_in_mod)
2653 {
2654 let has_impls = self.krate.index.values().any(|idx_item| {
2655 if let ItemEnum::Impl(imp) = &idx_item.inner
2656 && let Some(for_id) = get_type_id(&imp.for_)
2657 {
2658 return for_id == *item_id_in_mod;
2659 }
2660 false
2661 });
2662 if has_impls {
2663 module_types_considered.insert(*item_id_in_mod);
2664 }
2665 }
2666 }
2667 }
2668
2669 let module_types_with_impls_count = module_types_considered.len();
2670 if module_types_with_impls_count <= 1 {
2671 return module_common_traits;
2672 }
2673
2674 let mut trait_format_counts: HashMap<Id, HashMap<FormattedTraitImpl, usize>> =
2675 HashMap::new();
2676
2677 for item_id_in_mod in &module_types_considered {
2678 for krate_item in self.krate.index.values() {
2679 if let ItemEnum::Impl(imp) = &krate_item.inner
2680 && let Some(for_id) = get_type_id(&imp.for_)
2681 && for_id == *item_id_in_mod
2682 && let Some(trait_path) = &imp.trait_
2683 {
2684 let norm_impl =
2685 FormattedTraitImpl::from_impl(imp, None, trait_path, self.krate, self);
2686 *trait_format_counts
2687 .entry(trait_path.id)
2688 .or_default()
2689 .entry(norm_impl)
2690 .or_insert(0) += 1;
2691 }
2692 }
2693 }
2694
2695 let type_count_threshold = (module_types_with_impls_count as f32 * 0.5).ceil() as usize;
2696 debug!(
2697 "Module {:?} common trait threshold (types implementing): {} (out of {} types in module)",
2698 module_id, type_count_threshold, module_types_with_impls_count
2699 );
2700
2701 for (trait_id, format_map) in trait_format_counts {
2702 let total_implementations_for_trait = format_map.values().sum::<usize>();
2703
2704 if total_implementations_for_trait < type_count_threshold {
2705 trace!(
2706 "Module {:?} trait ID {:?} not common enough ({} implementations, need {})",
2707 module_id, trait_id, total_implementations_for_trait, type_count_threshold
2708 );
2709 continue;
2710 }
2711
2712 let mut has_positive = false;
2713 let mut has_negative = false;
2714 for f_impl in format_map.keys() {
2715 if f_impl.is_negative {
2716 has_negative = true;
2717 } else {
2718 has_positive = true;
2719 }
2720 }
2721 if has_positive && has_negative {
2722 warn!(
2723 "Module {:?} trait ID {:?} has mixed positive and negative implementations, cannot be common.",
2724 module_id, trait_id
2725 );
2726 continue;
2727 }
2728
2729 if format_map.len() == 1 {
2730 if let Some(formatted_impl) = format_map.keys().next()
2731 && module_common_traits.insert(formatted_impl.clone())
2732 {
2733 debug!(
2734 "Identified module-specific common trait (single format) for {:?}: {:?} for trait ID {:?}",
2735 self.krate
2736 .paths
2737 .get(module_id)
2738 .map(|p| p.path.join("::"))
2739 .unwrap_or_default(),
2740 formatted_impl.formatted_markdown_list_entry,
2741 trait_id
2742 );
2743 }
2744 } else {
2745 let simple_format_count = format_map
2746 .iter()
2747 .find(|(f_impl, _)| f_impl.category == TraitImplCategory::Simple)
2748 .map_or(0, |(_, count)| *count);
2749
2750 if simple_format_count * 2 > total_implementations_for_trait {
2751 if let Some(simple_impl) = format_map
2752 .keys()
2753 .find(|f_impl| f_impl.category == TraitImplCategory::Simple)
2754 && module_common_traits.insert(simple_impl.clone())
2755 {
2756 debug!(
2757 "Identified module-specific common trait (simple format predominant) for {:?}: {:?} for trait ID {:?}",
2758 self.krate
2759 .paths
2760 .get(module_id)
2761 .map(|p| p.path.join("::"))
2762 .unwrap_or_default(),
2763 simple_impl.formatted_markdown_list_entry,
2764 trait_id
2765 );
2766 }
2767 } else {
2768 trace!(
2769 "Module {:?} trait ID {:?} has multiple formats, but Simple is not predominant ({} of {}).",
2770 module_id, trait_id, simple_format_count, total_implementations_for_trait
2771 );
2772 }
2773 }
2774 }
2775 module_common_traits
2776 }
2777
2778 fn build_module_tree(krate: &'a Crate) -> ModuleTree {
2780 let mut tree = ModuleTree::default();
2781 let mut parent_map: HashMap<Id, Id> = HashMap::new(); for (id, item) in &krate.index {
2784 if let ItemEnum::Module(module_data) = &item.inner {
2785 tree.all_modules.insert(*id);
2786 let mut children = Vec::new();
2787 for child_id in &module_data.items {
2788 if let Some(child_item) = krate.index.get(child_id)
2789 && let ItemEnum::Module(_) = child_item.inner
2790 {
2791 children.push(*child_id);
2792 parent_map.insert(*child_id, *id);
2793 }
2794 }
2795 if !children.is_empty() {
2796 children.sort_by_key(|child_id| {
2798 krate
2799 .paths
2800 .get(child_id)
2801 .map(|p| p.path.join("::"))
2802 .unwrap_or_default()
2803 });
2804 tree.children.insert(*id, children);
2805 }
2806 }
2807 }
2808
2809 for module_id in &tree.all_modules {
2811 if *module_id != krate.root && !parent_map.contains_key(module_id) {
2812 tree.top_level_modules.push(*module_id);
2813 }
2814 }
2815
2816 tree.top_level_modules.sort_by_key(|id| {
2818 krate
2819 .paths
2820 .get(id)
2821 .map(|p| p.path.join("::"))
2822 .unwrap_or_default()
2823 });
2824
2825 tree
2826 }
2827
2828 fn get_current_header_level(&self) -> usize {
2830 self.doc_path.len() + 1 }
2832
2833 fn get_header_prefix(&self) -> String {
2836 let level = self.get_current_header_level();
2837 if self.doc_path.is_empty() || level < 2 {
2838 return String::new(); }
2840
2841 if level == 2 {
2842 format!("{}:", self.doc_path.last().unwrap_or(&0))
2844 } else {
2845 self.doc_path
2847 .iter()
2848 .map(|n| n.to_string())
2849 .collect::<Vec<_>>()
2850 .join(".")
2851 + ":"
2852 }
2853 }
2854
2855 fn get_template_marker(&self) -> String {
2857 if self.doc_path.is_empty() {
2858 "{{MISSING_DOCS}}".to_string()
2859 } else {
2860 format!(
2861 "{{{{MISSING_DOCS_{}}}}}", self.doc_path
2863 .iter()
2864 .map(|n| n.to_string())
2865 .collect::<Vec<_>>()
2866 .join("_")
2867 )
2868 }
2869 }
2870
2871 fn post_increment_current_level(&mut self) {
2875 if let Some(last) = self.doc_path.last_mut() {
2876 *last += 1;
2877 } else {
2878 warn!("Attempted to increment document path level when path was empty.");
2879 }
2880 }
2881
2882 fn push_level(&mut self) {
2884 self.doc_path.push(1);
2885 }
2886
2887 fn pop_level(&mut self) {
2889 self.doc_path.pop();
2890 }
2891
2892 fn get_item_kind(&self, id: &Id) -> Option<ItemKind> {
2893 self.krate
2895 .index
2896 .get(id)
2897 .map(Printer::infer_item_kind) .or_else(|| self.krate.paths.get(id).map(|summary| summary.kind))
2899 }
2900
2901 pub(crate) fn infer_item_kind(item: &Item) -> ItemKind {
2903 match item.inner {
2904 ItemEnum::Module(_) => ItemKind::Module,
2905 ItemEnum::ExternCrate { .. } => ItemKind::ExternCrate,
2906 ItemEnum::Use { .. } => ItemKind::Use, ItemEnum::Union(_) => ItemKind::Union,
2908 ItemEnum::Struct(_) => ItemKind::Struct,
2909 ItemEnum::StructField(_) => ItemKind::StructField,
2910 ItemEnum::Enum(_) => ItemKind::Enum,
2911 ItemEnum::Variant(_) => ItemKind::Variant,
2912 ItemEnum::Function(_) => ItemKind::Function,
2913 ItemEnum::Trait(_) => ItemKind::Trait,
2914 ItemEnum::TraitAlias(_) => ItemKind::TraitAlias,
2915 ItemEnum::Impl { .. } => ItemKind::Impl,
2916 ItemEnum::TypeAlias(_) => ItemKind::TypeAlias,
2917 ItemEnum::Constant { .. } => ItemKind::Constant, ItemEnum::Static(_) => ItemKind::Static,
2919 ItemEnum::ExternType => ItemKind::ExternType, ItemEnum::Macro(_) => ItemKind::Macro,
2921 ItemEnum::ProcMacro(ref pm) => match pm.kind {
2922 rustdoc_types::MacroKind::Bang => ItemKind::Macro, rustdoc_types::MacroKind::Attr => ItemKind::ProcAttribute,
2924 rustdoc_types::MacroKind::Derive => ItemKind::ProcDerive,
2925 },
2926 ItemEnum::Primitive(_) => ItemKind::Primitive,
2927 ItemEnum::AssocConst { .. } => ItemKind::AssocConst,
2928 ItemEnum::AssocType { .. } => ItemKind::AssocType,
2929 }
2930 }
2931
2932 fn print_docs(&mut self, item: &Item) {
2935 let header_level = self.get_current_header_level(); match (&item.docs, self.template_mode) {
2937 (Some(_), true) => {
2939 let marker = self.get_template_marker();
2940 writeln!(self.output, "{}\n", marker).unwrap();
2941 }
2942 (Some(docs), false) => {
2944 if !docs.trim().is_empty() {
2945 let adjusted_docs = adjust_markdown_headers(docs.trim(), header_level);
2947 writeln!(self.output, "{}\n", adjusted_docs).unwrap();
2948 }
2949 }
2951 (None, _) => {}
2953 }
2954 }
2955
2956 fn print_item_details(&mut self, id: &Id) -> bool {
2960 if !self.selected_ids.contains(id) {
2961 return false; }
2963
2964 let Some(item) = self.krate.index.get(id) else {
2965 warn!("Item details for ID {id:?} not found in index");
2966 return false;
2967 };
2968
2969 if matches!(item.inner, ItemEnum::Use(_) | ItemEnum::Module(_)) {
2972 return false;
2973 }
2974
2975 let item_header_level = self.get_current_header_level();
2976 let header_prefix = self.get_header_prefix();
2977 let declaration = generate_item_declaration(item, self.krate, &self.current_module_path);
2978
2979 if let Some(existing_prefix) = self.printed_ids.get(id) {
2980 writeln!(
2985 self.output,
2986 "\n{} {} `{}` (See section {} for details)\n",
2987 "#".repeat(item_header_level),
2988 header_prefix,
2989 declaration,
2990 existing_prefix
2991 )
2992 .unwrap();
2993 return false; }
2996
2997 self.printed_ids.insert(*id, header_prefix.clone());
2999
3000 writeln!(
3002 self.output,
3003 "\n{} {} `{}`\n", "#".repeat(item_header_level),
3005 header_prefix,
3006 declaration
3007 )
3008 .unwrap();
3009
3010 self.push_level();
3011
3012 let code_block = match &item.inner {
3014 ItemEnum::Struct(s) => Some(generate_struct_code_block(item, s, self.krate)),
3015 ItemEnum::Enum(e) => Some(generate_enum_code_block(item, e, self.krate)),
3016 ItemEnum::Union(u) => Some(generate_union_code_block(item, u, self.krate)),
3017 ItemEnum::Trait(t) => Some(generate_trait_code_block(item, t, self.krate)),
3018 ItemEnum::Function(f) => {
3019 let has_attrs = f.header.is_const
3021 || f.header.is_async
3022 || f.header.is_unsafe
3023 || !matches!(f.header.abi, Abi::Rust)
3024 || !item.attrs.is_empty(); let has_where = !f.generics.where_predicates.is_empty();
3026 if has_attrs || has_where {
3027 Some(generate_function_code_block(item, f, self.krate))
3028 } else {
3029 None }
3031 }
3032 _ => None,
3034 };
3035
3036 if let Some(code) = code_block {
3037 writeln!(self.output, "```rust\n{}\n```\n", code).unwrap();
3038 }
3039
3040 let has_stripped = matches!(
3041 &item.inner,
3042 ItemEnum::Struct(Struct {
3043 kind: StructKind::Plain {
3044 has_stripped_fields: true,
3045 ..
3046 },
3047 ..
3048 })
3049 );
3050
3051 if has_stripped {
3052 writeln!(self.output, "_[Private fields hidden]_\n").unwrap();
3053 }
3054
3055 self.print_docs(item);
3057
3058 match &item.inner {
3059 ItemEnum::Struct(s) => self.print_struct_fields(item, s),
3060 ItemEnum::Enum(e) => self.print_enum_variants(item, e),
3061 ItemEnum::Union(u) => self.print_union_fields(item, u),
3062 ItemEnum::Trait(t) => self.print_trait_associated_items(item, t),
3063 _ => {}
3065 }
3066
3067 let impl_ids = match &item.inner {
3069 ItemEnum::Struct(s) => Some(&s.impls),
3070 ItemEnum::Enum(e) => Some(&e.impls),
3071 ItemEnum::Trait(t) => Some(&t.implementations), ItemEnum::Union(u) => Some(&u.impls),
3073 ItemEnum::Primitive(p) => Some(&p.impls),
3074 _ => None,
3075 };
3076
3077 if let Some(ids) = impl_ids {
3078 match &item.inner {
3079 ItemEnum::Trait(_) => self.print_trait_implementors(ids, item),
3080 _ => self.print_item_implementations(ids, item),
3081 }
3082 }
3083
3084 self.pop_level();
3085
3086 true }
3088
3089 #[allow(unused)]
3091 fn has_documented_fields(&self, s: &Struct) -> bool {
3092 let field_ids = match &s.kind {
3093 StructKind::Plain { fields, .. } => fields.clone(),
3094 StructKind::Tuple(fields) => fields.iter().filter_map(|opt_id| *opt_id).collect(),
3095 StructKind::Unit => vec![],
3096 };
3097 field_ids.iter().any(|field_id| {
3098 self.selected_ids.contains(field_id)
3099 && self.krate.index.get(field_id).is_some_and(|item| {
3100 (self.template_mode && item.docs.is_some()) || has_docs(item)
3102 })
3103 })
3104 }
3105
3106 fn print_struct_fields(&mut self, _item: &Item, s: &Struct) {
3109 let all_field_ids: Vec<Id> = match &s.kind {
3110 StructKind::Plain { fields, .. } => fields.clone(),
3111 StructKind::Tuple(fields) => fields.iter().filter_map(|opt_id| *opt_id).collect(),
3112 StructKind::Unit => vec![],
3113 };
3114
3115 let mut has_printable_field = false;
3116
3117 for field_id in &all_field_ids {
3119 if !self.selected_ids.contains(field_id) {
3120 continue; }
3122
3123 if let Some(item) = self.krate.index.get(field_id) {
3124 let field_has_printable_docs =
3125 (self.template_mode && item.docs.is_some()) || has_docs(item);
3126 if field_has_printable_docs {
3127 if !self.printed_ids.contains_key(field_id) {
3129 has_printable_field = true;
3130 }
3131 } else {
3132 self.printed_ids.insert(*field_id, self.get_header_prefix());
3134 }
3135 } else {
3136 self.printed_ids.insert(*field_id, self.get_header_prefix());
3138 }
3139 }
3140
3141 if !has_printable_field {
3143 return;
3144 }
3145
3146 let fields_header_level = self.get_current_header_level();
3147 let header_prefix = self.get_header_prefix();
3148 writeln!(
3149 self.output,
3150 "{} {} Fields\n", "#".repeat(fields_header_level),
3152 header_prefix
3153 )
3154 .unwrap();
3155
3156 self.push_level();
3158 for field_id in &all_field_ids {
3159 if self.print_field_details(field_id) {
3160 self.post_increment_current_level();
3161 }
3162 }
3163 self.pop_level(); self.post_increment_current_level();
3166 }
3167
3168 fn print_union_fields(&mut self, _item: &Item, u: &Union) {
3170 let all_field_ids: Vec<Id> = u.fields.clone();
3171 let mut has_printable_field = false;
3172
3173 for field_id in &all_field_ids {
3174 if !self.selected_ids.contains(field_id) {
3175 continue;
3176 }
3177 if let Some(item) = self.krate.index.get(field_id) {
3178 let field_has_printable_docs =
3179 (self.template_mode && item.docs.is_some()) || has_docs(item);
3180 if field_has_printable_docs {
3181 if !self.printed_ids.contains_key(field_id) {
3182 has_printable_field = true;
3183 }
3184 } else {
3185 self.printed_ids.insert(*field_id, self.get_header_prefix());
3186 }
3187 } else {
3188 self.printed_ids.insert(*field_id, self.get_header_prefix());
3189 }
3190 }
3191
3192 if !has_printable_field && !u.has_stripped_fields {
3193 return;
3194 }
3195
3196 let fields_header_level = self.get_current_header_level();
3197 let header_prefix = self.get_header_prefix();
3198 writeln!(
3199 self.output,
3200 "{} {} Fields\n",
3201 "#".repeat(fields_header_level),
3202 header_prefix
3203 )
3204 .unwrap();
3205
3206 self.push_level();
3207 for field_id in &all_field_ids {
3208 if self.print_field_details(field_id) {
3209 self.post_increment_current_level();
3210 }
3211 }
3212 if u.has_stripped_fields {
3213 writeln!(self.output, "_[Private fields hidden]_").unwrap();
3214 }
3215 self.pop_level();
3216 self.post_increment_current_level();
3217 }
3218
3219 fn print_field_details(&mut self, field_id: &Id) -> bool {
3222 if !self.selected_ids.contains(field_id) || self.printed_ids.contains_key(field_id) {
3223 return false; }
3225
3226 if let Some(item) = self.krate.index.get(field_id) {
3227 let field_has_printable_docs =
3228 (self.template_mode && item.docs.is_some()) || has_docs(item);
3229
3230 if !field_has_printable_docs {
3232 return false;
3234 }
3235
3236 let header_prefix = self.get_header_prefix();
3237 self.printed_ids.insert(*field_id, header_prefix.clone());
3239
3240 if let ItemEnum::StructField(_field_type) = &item.inner {
3241 let name = item.name.as_deref().unwrap_or("_");
3242 let field_header_level = self.get_current_header_level();
3243
3244 writeln!(
3246 self.output,
3247 "{} {} `{}`\n", "#".repeat(field_header_level),
3249 header_prefix,
3250 name
3251 )
3252 .unwrap();
3253
3254 self.print_docs(item);
3256
3257 return true; }
3261 }
3262 self.printed_ids.insert(*field_id, self.get_header_prefix());
3264 false
3265 }
3266
3267 fn print_variant_field_details(&mut self, field_id: &Id) -> bool {
3270 if !self.selected_ids.contains(field_id) || self.printed_ids.contains_key(field_id) {
3271 return false; }
3273
3274 if let Some(item) = self.krate.index.get(field_id) {
3275 let field_has_printable_docs =
3276 (self.template_mode && item.docs.is_some()) || has_docs(item);
3277
3278 if !field_has_printable_docs {
3280 return false;
3282 }
3283 let header_prefix = self.get_header_prefix();
3284 self.printed_ids.insert(*field_id, header_prefix.clone());
3286
3287 if let ItemEnum::StructField(_field_type) = &item.inner {
3288 let name = item.name.as_deref().unwrap_or("_"); let field_header_level = self.get_current_header_level();
3290
3291 let header_name = if name == "_" || name.chars().all(|c| c.is_ascii_digit()) {
3294 format!("Field {}", name)
3295 } else {
3296 name.to_string()
3297 };
3298 writeln!(
3299 self.output,
3300 "{} {} `{}`\n", "#".repeat(field_header_level),
3302 header_prefix,
3303 header_name
3304 )
3305 .unwrap();
3306
3307 self.print_docs(item);
3309
3310 self.post_increment_current_level();
3312
3313 return true; }
3317 }
3318 self.printed_ids.insert(*field_id, self.get_header_prefix());
3320 false
3321 }
3322
3323 #[allow(unused)]
3325 fn has_printable_variants(&self, e: &Enum) -> bool {
3326 e.variants.iter().any(|variant_id| {
3327 if !self.selected_ids.contains(variant_id) {
3328 return false;
3329 }
3330 if let Some(item) = self.krate.index.get(variant_id) {
3331 if (self.template_mode && item.docs.is_some()) || has_docs(item) {
3333 return true;
3334 }
3335 if let ItemEnum::Variant(v) = &item.inner {
3337 let field_ids: Vec<Id> = match &v.kind {
3338 VariantKind::Plain => vec![],
3339 VariantKind::Tuple(fields) => {
3340 fields.iter().filter_map(|opt_id| *opt_id).collect()
3341 }
3342 VariantKind::Struct { fields, .. } => fields.clone(),
3343 };
3344 for field_id in field_ids {
3345 if self.selected_ids.contains(&field_id)
3346 && let Some(f_item) = self.krate.index.get(&field_id)
3347 && ((self.template_mode && f_item.docs.is_some()) || has_docs(f_item))
3348 {
3349 return true;
3350 }
3351 }
3352 }
3353 }
3354 false
3355 })
3356 }
3357
3358 fn print_enum_variants(&mut self, _item: &Item, e: &Enum) {
3361 let mut has_printable_variant_or_field = false;
3362 let mut printed_any_variant = false;
3363
3364 for variant_id in &e.variants {
3366 if !self.selected_ids.contains(variant_id) {
3367 continue; }
3369
3370 if let Some(item) = self.krate.index.get(variant_id) {
3371 let variant_has_printable_docs =
3372 (self.template_mode && item.docs.is_some()) || has_docs(item);
3373 let mut variant_has_printable_field = false;
3374
3375 if let ItemEnum::Variant(v) = &item.inner {
3377 let field_ids: Vec<Id> = match &v.kind {
3378 VariantKind::Plain => vec![],
3379 VariantKind::Tuple(fields) => {
3380 fields.iter().filter_map(|opt_id| *opt_id).collect()
3381 }
3382 VariantKind::Struct { fields, .. } => fields.clone(),
3383 };
3384
3385 for field_id in field_ids {
3386 if self.selected_ids.contains(&field_id) {
3387 let field_has_printable_docs =
3388 self.krate.index.get(&field_id).is_some_and(|f_item| {
3389 (self.template_mode && f_item.docs.is_some())
3390 || has_docs(f_item)
3391 });
3392 if field_has_printable_docs {
3393 if !self.printed_ids.contains_key(&field_id) {
3394 variant_has_printable_field = true;
3395 }
3396 } else {
3397 self.printed_ids.insert(field_id, self.get_header_prefix());
3398 }
3400 } else {
3401 self.printed_ids.insert(field_id, self.get_header_prefix());
3403 }
3404 }
3405 }
3406
3407 if variant_has_printable_docs || variant_has_printable_field {
3408 if !self.printed_ids.contains_key(variant_id) {
3410 has_printable_variant_or_field = true;
3411 }
3412 } else {
3413 self.printed_ids
3415 .insert(*variant_id, self.get_header_prefix());
3416 }
3417 } else {
3418 self.printed_ids
3420 .insert(*variant_id, self.get_header_prefix());
3421 }
3422 }
3423
3424 if !has_printable_variant_or_field && !e.has_stripped_variants {
3426 return;
3427 }
3428
3429 let variants_header_level = self.get_current_header_level();
3430 let header_prefix = self.get_header_prefix();
3431 writeln!(
3432 self.output,
3433 "{} {} Variants\n", "#".repeat(variants_header_level),
3435 header_prefix
3436 )
3437 .unwrap();
3438
3439 self.push_level();
3441 for variant_id in &e.variants {
3443 if self.print_variant_details(variant_id) {
3444 printed_any_variant = true;
3445 }
3446 }
3447
3448 if e.has_stripped_variants {
3449 if printed_any_variant {
3451 writeln!(self.output).unwrap();
3452 }
3453 writeln!(self.output, "_[Private variants hidden]_").unwrap();
3454 }
3455 self.pop_level(); self.post_increment_current_level();
3457 }
3458
3459 fn print_variant_details(&mut self, variant_id: &Id) -> bool {
3462 if !self.selected_ids.contains(variant_id) {
3463 if self.printed_ids.contains_key(variant_id) {
3467 return false;
3468 }
3469 } else if self.printed_ids.contains_key(variant_id) {
3470 return false;
3472 }
3473
3474 if let Some(item) = self.krate.index.get(variant_id)
3475 && let ItemEnum::Variant(variant_data) = &item.inner
3476 {
3477 let variant_has_printable_docs =
3478 (self.template_mode && item.docs.is_some()) || has_docs(item);
3479 let mut printable_fields = Vec::new();
3480 let mut printed_any_field = false;
3481
3482 let (field_ids, stripped) = match &variant_data.kind {
3484 VariantKind::Plain => (vec![], false),
3485 VariantKind::Tuple(fields) => {
3486 (fields.iter().filter_map(|opt_id| *opt_id).collect(), false)
3487 }
3488 VariantKind::Struct {
3489 fields,
3490 has_stripped_fields: s,
3491 } => (fields.clone(), *s),
3492 };
3493
3494 for field_id in &field_ids {
3495 if self.selected_ids.contains(field_id) {
3496 let field_has_printable_docs =
3497 self.krate.index.get(field_id).is_some_and(|f_item| {
3498 (self.template_mode && f_item.docs.is_some()) || has_docs(f_item)
3499 });
3500 if field_has_printable_docs && !self.printed_ids.contains_key(field_id) {
3501 printable_fields.push(*field_id);
3502 } else {
3503 self.printed_ids.insert(*field_id, self.get_header_prefix());
3505 }
3506 } else {
3507 self.printed_ids.insert(*field_id, self.get_header_prefix());
3509 }
3510 }
3511
3512 if !variant_has_printable_docs && printable_fields.is_empty() {
3514 self.printed_ids
3516 .insert(*variant_id, self.get_header_prefix());
3517 return false;
3518 }
3519
3520 let header_prefix = self.get_header_prefix();
3521 self.printed_ids.insert(*variant_id, header_prefix.clone());
3523
3524 let signature = format_variant_signature(item, variant_data, self.krate);
3525 let variant_header_level = self.get_current_header_level();
3526
3527 writeln!(
3529 self.output,
3530 "{} {} `{}`\n", "#".repeat(variant_header_level),
3532 header_prefix,
3533 signature
3534 )
3535 .unwrap();
3536 self.push_level();
3537
3538 self.print_docs(item);
3540
3541 if !printable_fields.is_empty() || stripped {
3543 let field_section_level = self.get_current_header_level();
3544 let fields_header_prefix = self.get_header_prefix();
3545 writeln!(
3546 self.output,
3547 "{} {} Fields\n", "#".repeat(field_section_level),
3549 fields_header_prefix
3550 )
3551 .unwrap();
3552 self.push_level();
3553
3554 for field_id in printable_fields {
3555 if self.print_variant_field_details(&field_id) {
3556 printed_any_field = true;
3557 }
3558 }
3559
3560 if stripped {
3561 if printed_any_field {
3562 writeln!(self.output).unwrap(); }
3564 writeln!(self.output, "_[Private fields hidden]_").unwrap();
3565 }
3566 self.pop_level();
3567 }
3568
3569 self.pop_level();
3570 self.post_increment_current_level();
3571
3572 return true; }
3574 self.printed_ids
3576 .insert(*variant_id, self.get_header_prefix());
3577 false
3578 }
3579
3580 fn print_trait_associated_items(&mut self, _trait_item: &Item, t: &Trait) {
3582 let mut required_types = Vec::new();
3583 let mut required_methods = Vec::new();
3584 let mut provided_methods = Vec::new();
3585 let mut has_printable_assoc_item = false;
3586
3587 for item_id in &t.items {
3589 if !self.selected_ids.contains(item_id) {
3590 continue;
3591 }
3592 if !self.printed_ids.contains_key(item_id) {
3596 self.printed_ids.insert(*item_id, self.get_header_prefix());
3597 }
3598
3599 if let Some(assoc_item) = self.krate.index.get(item_id) {
3600 let item_has_printable_docs =
3601 (self.template_mode && assoc_item.docs.is_some()) || has_docs(assoc_item);
3602 if item_has_printable_docs {
3603 has_printable_assoc_item = true;
3604 }
3605
3606 match &assoc_item.inner {
3607 ItemEnum::AssocType { .. } => {
3608 required_types.push((*item_id, item_has_printable_docs));
3609 }
3610 ItemEnum::Function(f) => {
3611 if !f.has_body {
3612 required_methods.push((*item_id, item_has_printable_docs));
3613 } else {
3614 provided_methods.push((*item_id, item_has_printable_docs));
3615 }
3616 }
3617 ItemEnum::AssocConst { .. } => {
3618 required_types.push((*item_id, item_has_printable_docs));
3622 }
3623 _ => {} }
3625 }
3626 }
3627
3628 if !has_printable_assoc_item {
3630 return;
3631 }
3632
3633 required_types.sort_by_key(|(id, _)| self.krate.index.get(id).and_then(|i| i.name.clone()));
3635 required_methods
3636 .sort_by_key(|(id, _)| self.krate.index.get(id).and_then(|i| i.name.clone()));
3637 provided_methods
3638 .sort_by_key(|(id, _)| self.krate.index.get(id).and_then(|i| i.name.clone()));
3639
3640 if required_types.iter().any(|(_, has_docs)| *has_docs) {
3641 let sub_level = self.get_current_header_level();
3642 let sub_prefix = self.get_header_prefix();
3643 writeln!(
3644 self.output,
3645 "{} {} Required Associated Types\n",
3646 "#".repeat(sub_level),
3647 sub_prefix
3648 )
3649 .unwrap();
3650 self.push_level();
3651 for (id, has_docs) in required_types {
3652 if has_docs {
3653 self.print_associated_item_summary(&id);
3654 }
3655 }
3656 self.pop_level();
3657 self.post_increment_current_level();
3658 }
3659
3660 if required_methods.iter().any(|(_, has_docs)| *has_docs) {
3661 let sub_level = self.get_current_header_level();
3662 let sub_prefix = self.get_header_prefix();
3663 writeln!(
3664 self.output,
3665 "{} {} Required Methods\n",
3666 "#".repeat(sub_level),
3667 sub_prefix
3668 )
3669 .unwrap();
3670 self.push_level();
3671 for (id, has_docs) in required_methods {
3672 if has_docs {
3673 self.print_associated_item_summary(&id);
3674 }
3675 }
3676 self.pop_level();
3677 self.post_increment_current_level();
3678 }
3679
3680 if provided_methods.iter().any(|(_, has_docs)| *has_docs) {
3681 let sub_level = self.get_current_header_level();
3682 let sub_prefix = self.get_header_prefix();
3683 writeln!(
3684 self.output,
3685 "{} {} Provided Methods\n",
3686 "#".repeat(sub_level),
3687 sub_prefix
3688 )
3689 .unwrap();
3690 self.push_level();
3691 for (id, has_docs) in provided_methods {
3692 if has_docs {
3693 self.print_associated_item_summary(&id);
3694 }
3695 }
3696 self.pop_level();
3697 self.post_increment_current_level();
3698 }
3699 }
3700
3701 fn generate_associated_item_summary(&mut self, assoc_item_id: &Id) -> Option<String> {
3704 if !self.selected_ids.contains(assoc_item_id) {
3705 return None;
3706 }
3707 if let Some(item) = self.krate.index.get(assoc_item_id) {
3708 let mut summary = String::new();
3709 if let ItemEnum::Function(f) = &item.inner {
3714 let has_attrs = f.header.is_const
3715 || f.header.is_async
3716 || f.header.is_unsafe
3717 || !matches!(f.header.abi, Abi::Rust)
3718 || !item.attrs.is_empty(); let has_where = !f.generics.where_predicates.is_empty();
3720 if has_attrs || has_where {
3721 let code = generate_function_code_block(item, f, self.krate);
3722 writeln!(summary, "```rust\n{}\n```\n", code).unwrap();
3723 }
3724 }
3725
3726 let mut temp_printer = self.clone_with_new_output();
3729 temp_printer.doc_path = self.doc_path.clone();
3731 temp_printer.print_docs(item);
3732 write!(summary, "{}", temp_printer.output).unwrap();
3733
3734 match &item.inner {
3736 ItemEnum::AssocConst { type_, value } => {
3738 writeln!(summary, "_Type: `{}`_", format_type(type_, self.krate)).unwrap();
3739 if let Some(val) = value {
3740 writeln!(summary, "_Default: `{}`_\n", val).unwrap(); }
3742 }
3743 ItemEnum::AssocType { bounds, type_, .. } => {
3744 if !bounds.is_empty() {
3746 let bounds_str = bounds
3747 .iter()
3748 .map(|b| format_generic_bound(b, self.krate))
3749 .collect::<Vec<_>>()
3750 .join(" + ");
3751 writeln!(summary, "_Bounds: `{}`_", bounds_str).unwrap();
3752 }
3753 if let Some(ty) = type_ {
3754 writeln!(summary, "_Default: `{}`_\n", format_type(ty, self.krate))
3755 .unwrap(); }
3757 }
3758 _ => {}
3759 }
3760 Some(summary)
3761 } else {
3762 None
3763 }
3764 }
3765
3766 fn print_associated_item_summary(&mut self, assoc_item_id: &Id) {
3768 if let Some(item) = self.krate.index.get(assoc_item_id) {
3769 if let Some(summary) = self.generate_associated_item_summary(assoc_item_id) {
3771 let declaration =
3772 generate_item_declaration(item, self.krate, &self.current_module_path);
3773 let assoc_item_header_level = self.get_current_header_level();
3774 let header_prefix = self.get_header_prefix();
3775 writeln!(
3777 self.output,
3778 "{} {} `{}`\n", "#".repeat(assoc_item_header_level),
3780 header_prefix,
3781 declaration
3782 )
3783 .unwrap();
3784 if !summary.trim().is_empty() {
3786 writeln!(self.output, "{}", summary.trim()).unwrap();
3787 }
3788 writeln!(self.output).unwrap(); self.post_increment_current_level();
3791 }
3792 }
3795 }
3796
3797 fn format_trait_list(&mut self, traits_to_format: &[FormattedTraitImpl]) -> String {
3799 if traits_to_format.is_empty() {
3800 return String::new();
3801 }
3802
3803 let mut output = String::new();
3804 let mut simple_impls = Vec::new();
3805 let mut generic_or_complex_impls = Vec::new();
3806 let mut auto_traits = Vec::new();
3807 let mut blanket_impls = Vec::new();
3808
3809 for norm_trait in traits_to_format {
3810 match norm_trait.category {
3811 TraitImplCategory::Simple => simple_impls.push(norm_trait),
3812 TraitImplCategory::GenericOrComplex => generic_or_complex_impls.push(norm_trait),
3813 TraitImplCategory::Auto => auto_traits.push(norm_trait),
3814 TraitImplCategory::Blanket => blanket_impls.push(norm_trait),
3815 }
3816 }
3817
3818 simple_impls.sort_by_key(|t| &t.formatted_markdown_list_entry);
3820 generic_or_complex_impls.sort_by_key(|t| &t.formatted_markdown_list_entry);
3821 auto_traits.sort_by_key(|t| &t.formatted_markdown_list_entry);
3822 blanket_impls.sort_by_key(|t| &t.formatted_markdown_list_entry);
3823
3824 self.push_level();
3825 let mut preceding_section = false;
3826
3827 let mut print_section =
3828 |traits: &[&FormattedTraitImpl], current_output: &mut String, _section_name: &str| {
3829 if !traits.is_empty() {
3830 if preceding_section {
3831 writeln!(current_output).unwrap();
3832 }
3833 for norm_trait in traits {
3834 writeln!(
3835 current_output,
3836 "{}",
3837 norm_trait.formatted_markdown_list_entry
3838 )
3839 .unwrap();
3840 if let Some((trait_impl, impl_id)) = norm_trait.get_impl_data(self.krate) {
3841 self.printed_ids.insert(impl_id, self.get_header_prefix());
3842 for assoc_item_id in &trait_impl.items {
3843 if self.selected_ids.contains(assoc_item_id) {
3844 self.printed_ids
3845 .insert(*assoc_item_id, self.get_header_prefix());
3846 }
3847 }
3848 }
3849 self.post_increment_current_level();
3850 }
3851 preceding_section = true;
3852 }
3853 };
3854
3855 print_section(&simple_impls, &mut output, "Simple");
3856 print_section(&generic_or_complex_impls, &mut output, "Generic or Complex");
3857 print_section(&auto_traits, &mut output, "Auto");
3858 print_section(&blanket_impls, &mut output, "Blanket");
3859
3860 self.pop_level();
3861 output
3862 }
3863
3864 fn print_item_implementations(&mut self, impl_ids: &[Id], target_item: &Item) {
3866 let target_item_id = target_item.id;
3867 let target_name = target_item
3868 .name
3869 .as_deref()
3870 .unwrap_or(match &target_item.inner {
3871 ItemEnum::Primitive(Primitive { name, .. }) => name.as_str(),
3872 _ => "{unknown_item_type}",
3873 });
3874
3875 let mut item_specific_impl_data = Vec::new();
3876 for impl_id in impl_ids {
3877 if let Some(impl_item) = self.krate.index.get(impl_id)
3878 && self.selected_ids.contains(&impl_item.id)
3879 && let ItemEnum::Impl(imp) = &impl_item.inner
3880 {
3881 if get_type_id(&imp.for_) == Some(target_item_id) {
3883 item_specific_impl_data.push((impl_item, imp.clone()));
3884 }
3885 }
3886 }
3887
3888 let inherent_impl_items: Vec<_> = item_specific_impl_data
3890 .iter()
3891 .filter(|(_, imp)| imp.trait_.is_none())
3892 .collect();
3893
3894 if !inherent_impl_items.is_empty() {
3895 for (impl_item, imp) in inherent_impl_items {
3896 if self.printed_ids.contains_key(&impl_item.id) {
3897 continue;
3898 }
3899 self.print_impl_block_details(impl_item, imp);
3900 }
3901 }
3902
3903 let trait_impl_data: Vec<FormattedTraitImpl> = item_specific_impl_data
3905 .iter()
3906 .filter_map(|(impl_item, imp)| {
3907 if self.printed_ids.contains_key(&impl_item.id) {
3908 return None; }
3910 imp.trait_.as_ref().map(|tp| {
3911 FormattedTraitImpl::from_impl(imp, Some(impl_item.id), tp, self.krate, self)
3912 })
3913 })
3914 .collect();
3915
3916 if trait_impl_data.is_empty() {
3917 return;
3918 }
3919
3920 let current_module_id = self
3921 .current_module_path
3922 .last()
3923 .and_then(|mod_name| {
3924 self.resolved_modules
3925 .values()
3926 .find(|rm| {
3927 self.krate
3928 .paths
3929 .get(&rm.id)
3930 .is_some_and(|p| p.path.last() == Some(mod_name))
3931 })
3932 .map(|rm| rm.id)
3933 })
3934 .unwrap_or(self.krate.root);
3935
3936 let module_common_traits = self
3937 .module_common_traits
3938 .get(¤t_module_id)
3939 .cloned()
3940 .unwrap_or_default();
3941
3942 let mut non_common_trait_impls = Vec::new();
3943 let mut missing_module_common_trait_paths = HashSet::new(); for common_trait_format in &module_common_traits {
3946 missing_module_common_trait_paths.insert(common_trait_format.trait_id);
3947 }
3948
3949 for norm_trait in &trait_impl_data {
3950 if module_common_traits
3952 .iter()
3953 .any(|ct| ct.trait_id == norm_trait.trait_id)
3954 {
3955 missing_module_common_trait_paths.remove(&norm_trait.trait_id);
3957
3958 if !module_common_traits.contains(norm_trait) {
3960 non_common_trait_impls.push(norm_trait.clone());
3962 } else {
3963 if let Some((trait_impl, impl_id)) = norm_trait.get_impl_data(self.krate) {
3965 self.printed_ids.insert(impl_id, self.get_header_prefix());
3966 for assoc_item_id in &trait_impl.items {
3967 if self.selected_ids.contains(assoc_item_id) {
3968 self.printed_ids
3969 .insert(*assoc_item_id, self.get_header_prefix());
3970 }
3971 }
3972 }
3973 }
3974 } else {
3975 non_common_trait_impls.push(norm_trait.clone());
3977 }
3978 }
3979
3980 if !non_common_trait_impls.is_empty() || !missing_module_common_trait_paths.is_empty() {
3981 let trait_impl_header_level = self.get_current_header_level();
3982 let header_prefix = self.get_header_prefix();
3983 writeln!(
3984 self.output,
3985 "{} {} Trait Implementations for `{}`\n",
3986 "#".repeat(trait_impl_header_level),
3987 header_prefix,
3988 target_name
3989 )
3990 .unwrap();
3991
3992 if !missing_module_common_trait_paths.is_empty() {
3993 let mut sorted_missing_common_trait_names: Vec<String> =
3994 missing_module_common_trait_paths
3995 .iter()
3996 .filter_map(|trait_id| {
3997 module_common_traits
4000 .iter()
4001 .find(|ct| ct.trait_id == *trait_id)
4002 .map(|ct| {
4003 ct.formatted_markdown_list_entry
4004 .split_once("`")
4005 .and_then(|(_, rest)| rest.split_once("`"))
4006 .map(|(path, _)| path.to_string())
4007 .unwrap_or_else(|| {
4008 format_id_path_canonical(trait_id, self.krate)
4009 }) })
4011 })
4012 .collect();
4013 sorted_missing_common_trait_names.sort_unstable();
4014 if !sorted_missing_common_trait_names.is_empty() {
4015 writeln!(
4016 self.output,
4017 "**(Note: Does not implement common trait(s): `{}`)**\n",
4018 sorted_missing_common_trait_names.join("`, `")
4019 )
4020 .unwrap();
4021 }
4022 }
4023
4024 let formatted_list = self.format_trait_list(&non_common_trait_impls);
4025 if !formatted_list.is_empty() {
4026 write!(self.output, "{}", formatted_list).unwrap();
4027 }
4028
4029 self.post_increment_current_level();
4030 }
4031 }
4032
4033 fn print_trait_implementors(&mut self, impl_ids: &[Id], _trait_item: &Item) {
4035 let implementors: Vec<&Item> = impl_ids
4036 .iter()
4037 .filter_map(|id| self.krate.index.get(id))
4038 .filter(|item| {
4039 self.selected_ids.contains(&item.id) && matches!(item.inner, ItemEnum::Impl(_))
4040 })
4041 .collect();
4042
4043 if !implementors.is_empty() {
4044 let implementors_section_level = self.get_current_header_level();
4045 let header_prefix = self.get_header_prefix();
4046 writeln!(
4047 self.output,
4048 "{} {} Implementors\n",
4049 "#".repeat(implementors_section_level),
4050 header_prefix
4051 )
4052 .unwrap();
4053
4054 self.push_level();
4055 for impl_item in implementors {
4056 if let ItemEnum::Impl(imp) = &impl_item.inner {
4057 let impl_header_only = format_impl_decl_header_only(imp, self.krate);
4058 let impl_header_level = self.get_current_header_level();
4059 let impl_prefix = self.get_header_prefix();
4060
4061 writeln!(
4062 self.output,
4063 "{} {} `{}`\n",
4064 "#".repeat(impl_header_level),
4065 impl_prefix,
4066 impl_header_only.trim()
4067 )
4068 .unwrap();
4069
4070 if !imp.generics.where_predicates.is_empty() {
4072 let where_clause =
4073 format_generics_where_only(&imp.generics.where_predicates, self.krate);
4074 writeln!(self.output, "```rust\n{}\n```\n", where_clause).unwrap();
4075 }
4076
4077 let mut temp_printer = self.clone_with_new_output();
4079 temp_printer.doc_path = self.doc_path.clone();
4080 temp_printer.print_docs(impl_item);
4081 write!(self.output, "{}", temp_printer.output).unwrap();
4082
4083 self.printed_ids
4085 .insert(impl_item.id, self.get_header_prefix());
4086 for assoc_item_id in &imp.items {
4087 if self.selected_ids.contains(assoc_item_id) {
4088 self.printed_ids
4089 .insert(*assoc_item_id, self.get_header_prefix());
4090 }
4091 }
4092
4093 self.post_increment_current_level();
4094 }
4095 }
4096 self.pop_level();
4097 self.post_increment_current_level();
4098 }
4099 }
4100
4101 fn print_impl_block_details(&mut self, impl_item: &Item, imp: &Impl) {
4104 let header_prefix = self.get_header_prefix();
4105 if self
4107 .printed_ids
4108 .insert(impl_item.id, header_prefix.clone())
4109 .is_some()
4110 {
4111 return;
4114 }
4115
4116 self.post_increment_current_level();
4118 let impl_header_level = self.get_current_header_level();
4119 let impl_header = format_impl_decl(imp, self.krate);
4120
4121 writeln!(
4123 self.output,
4124 "{} {} `{}`\n", "#".repeat(impl_header_level),
4126 header_prefix, impl_header.trim() )
4129 .unwrap();
4130
4131 let mut temp_printer = self.clone_with_new_output();
4134 temp_printer.doc_path = self.doc_path.clone();
4136 temp_printer.print_docs(impl_item);
4137 write!(self.output, "{}", temp_printer.output).unwrap();
4138
4139 let mut assoc_consts = vec![];
4141 let mut assoc_types = vec![];
4142 let mut assoc_fns = vec![];
4143 for assoc_item_id in &imp.items {
4144 if !self.selected_ids.contains(assoc_item_id) {
4146 continue;
4147 }
4148
4149 if let Some(assoc_item) = self.krate.index.get(assoc_item_id) {
4150 match &assoc_item.inner {
4151 ItemEnum::AssocConst { .. } => assoc_consts.push(assoc_item_id),
4152 ItemEnum::AssocType { .. } => assoc_types.push(assoc_item_id),
4153 ItemEnum::Function(_) => assoc_fns.push(assoc_item_id),
4154 _ => {} }
4156 }
4157 }
4158
4159 self.push_level();
4161
4162 if !assoc_consts.is_empty() {
4163 for id in assoc_consts {
4164 self.print_associated_item_summary(id);
4165 if !self.printed_ids.contains_key(id) {
4166 self.printed_ids.insert(*id, self.get_header_prefix());
4167 }
4168 }
4169 }
4170 if !assoc_types.is_empty() {
4171 for id in assoc_types {
4172 self.print_associated_item_summary(id);
4173 if !self.printed_ids.contains_key(id) {
4174 self.printed_ids.insert(*id, self.get_header_prefix());
4175 }
4176 }
4177 }
4178 if !assoc_fns.is_empty() {
4179 for id in assoc_fns {
4180 self.print_associated_item_summary(id);
4181 if !self.printed_ids.contains_key(id) {
4182 self.printed_ids.insert(*id, self.get_header_prefix());
4183 }
4184 }
4185 }
4186
4187 self.pop_level(); }
4189
4190 fn print_items_of_kind(&mut self, item_ids: &[Id], kind: ItemKind, header_name: &str) -> bool {
4192 let mut items_to_print: Vec<&Id> = item_ids
4194 .iter()
4195 .filter(|id| self.selected_ids.contains(id))
4196 .filter(|id| self.get_item_kind(id) == Some(kind))
4197 .collect();
4198
4199 if items_to_print.is_empty() {
4200 return false; }
4202
4203 items_to_print
4204 .sort_by_key(|id| self.krate.index.get(id).and_then(|item| item.name.clone()));
4205
4206 let section_header_level = self.get_current_header_level();
4207 let header_prefix = self.get_header_prefix();
4208 writeln!(
4209 self.output,
4210 "\n{} {} {}",
4211 "#".repeat(section_header_level),
4212 header_prefix,
4213 header_name
4214 )
4215 .unwrap();
4216
4217 self.push_level();
4218 for id in items_to_print {
4220 if self.print_item_details(id) {
4222 self.post_increment_current_level();
4223 } else {
4224 self.post_increment_current_level();
4232 }
4233 }
4234 self.pop_level(); true
4237 }
4238
4239 fn print_module_contents(&mut self, module_id: &Id) {
4242 if let Some(resolved_module) = self.resolved_modules.get(module_id) {
4243 let mut items_by_kind: HashMap<ItemKind, Vec<Id>> = HashMap::new();
4244 let mut cross_referenced_items: Vec<(Id, String, String)> = Vec::new(); for id in &resolved_module.items {
4247 if !self.selected_ids.contains(id) {
4248 continue;
4249 }
4250
4251 if let Some(existing_prefix) = self.printed_ids.get(id) {
4252 if let Some(item) = self.krate.index.get(id) {
4253 if !matches!(
4255 item.inner,
4256 ItemEnum::Impl(_)
4257 | ItemEnum::Use { .. }
4258 | ItemEnum::StructField(_)
4259 | ItemEnum::Variant(_) | ItemEnum::AssocConst { .. } | ItemEnum::AssocType { .. }
4262 | ItemEnum::Module(_)
4263 ) {
4264 let decl = generate_item_declaration(
4265 item,
4266 self.krate,
4267 &self.current_module_path,
4268 );
4269 cross_referenced_items.push((*id, decl, existing_prefix.clone()));
4270 }
4271 }
4272 continue; }
4274
4275 if let Some(kind) = self.get_item_kind(id) {
4276 match kind {
4277 ItemKind::Impl
4278 | ItemKind::Variant | ItemKind::StructField | ItemKind::AssocConst | ItemKind::AssocType | ItemKind::Use | ItemKind::Module => continue, _ => {}
4285 }
4286 items_by_kind.entry(kind).or_default().push(*id);
4287 }
4288 }
4289
4290 for ids in items_by_kind.values_mut() {
4292 ids.sort_by_key(|id| self.krate.index.get(id).and_then(|item| item.name.clone()));
4293 }
4294 cross_referenced_items.sort_by_key(|(_, decl, _)| decl.clone());
4295
4296 let print_order = [
4297 (ItemKind::Macro, "Macros"),
4298 (ItemKind::ProcAttribute, "Attribute Macros"),
4299 (ItemKind::ProcDerive, "Derive Macros"),
4300 (ItemKind::Struct, "Structs"),
4301 (ItemKind::Enum, "Enums"),
4302 (ItemKind::Union, "Unions"),
4303 (ItemKind::Trait, "Traits"),
4304 (ItemKind::Function, "Functions"),
4305 (ItemKind::TypeAlias, "Type Aliases"),
4306 (ItemKind::TraitAlias, "Trait Aliases"),
4307 (ItemKind::Static, "Statics"),
4308 (ItemKind::Constant, "Constants"),
4309 (ItemKind::ExternCrate, "External Crates"),
4310 (ItemKind::ExternType, "External Types"),
4311 (ItemKind::Primitive, "Primitives"),
4312 ];
4313
4314 for (kind, header_name) in print_order {
4315 if let Some(ids) = items_by_kind.get(&kind) {
4316 if ids.is_empty() {
4317 continue;
4318 }
4319 if self.print_items_of_kind(ids, kind, header_name) {
4320 self.post_increment_current_level();
4321 }
4322 }
4323 }
4324
4325 if !cross_referenced_items.is_empty() {
4327 let re_exports_header_level = self.get_current_header_level();
4328 let re_exports_prefix = self.get_header_prefix();
4329 writeln!(
4330 self.output,
4331 "\n{} {} Re-exports\n",
4332 "#".repeat(re_exports_header_level),
4333 re_exports_prefix
4334 )
4335 .unwrap();
4336 for (_id, declaration, original_prefix) in cross_referenced_items {
4337 writeln!(
4338 self.output,
4339 "- `{}` (See section {} for details)",
4340 declaration, original_prefix
4341 )
4342 .unwrap();
4343 }
4344 writeln!(self.output).unwrap(); self.post_increment_current_level();
4346 }
4347 } else {
4348 warn!(
4349 "Could not find resolved module data for ID: {:?}",
4350 module_id
4351 );
4352 }
4353 }
4354
4355 fn print_graph_context(&mut self, id: &Id) {
4357 let incoming_edges_data: Vec<Edge> = self
4359 .graph
4360 .find_incoming_edges(id)
4361 .into_iter()
4362 .cloned()
4363 .collect();
4364
4365 if !incoming_edges_data.is_empty() {
4366 writeln!(self.output, "_Referenced by:_").unwrap();
4367 let mut sorted_edges = incoming_edges_data;
4369 sorted_edges.sort_by_key(|edge| {
4370 (
4371 format_id_path_canonical(&edge.source, self.krate),
4372 format!("{:?}", edge.label),
4373 )
4374 });
4375
4376 self.push_level();
4378 for edge in sorted_edges {
4379 self.post_increment_current_level(); let source_path = format_id_path_canonical(&edge.source, self.krate);
4381 let template_marker = if self.template_mode
4382 && self
4383 .krate
4384 .index
4385 .get(&edge.source)
4386 .is_some_and(|i| i.docs.is_some())
4387 {
4388 format!("\n {}", self.get_template_marker())
4389 } else {
4390 "".to_string()
4391 };
4392 writeln!(
4393 self.output,
4394 "- `{}` ({}){}",
4395 source_path,
4396 edge.label, template_marker
4398 )
4399 .unwrap();
4400 }
4401 self.pop_level(); writeln!(self.output).unwrap(); } else {
4404 writeln!(
4405 self.output,
4406 "_Item has no known incoming references in the graph._\n"
4407 )
4408 .unwrap();
4409 }
4410 }
4411
4412 fn clone_with_new_output(&self) -> Self {
4414 Printer {
4415 krate: self.krate,
4416 manifest_data: self.manifest_data.clone(),
4417 paths: self.paths.clone(),
4418 crate_extra: self.crate_extra.clone(),
4419 include_other: self.include_other,
4420 template_mode: self.template_mode,
4421 no_common_traits: self.no_common_traits,
4422 selected_ids: self.selected_ids.clone(), resolved_modules: self.resolved_modules.clone(),
4424 graph: self.graph.clone(),
4425 printed_ids: self.printed_ids.clone(),
4426 output: String::new(), module_tree: self.module_tree.clone(),
4428 doc_path: self.doc_path.clone(),
4429 current_module_path: self.current_module_path.clone(),
4430 crate_common_traits: self.crate_common_traits.clone(),
4431 all_type_ids_with_impls: self.all_type_ids_with_impls.clone(),
4432 module_common_traits: self.module_common_traits.clone(),
4433 }
4434 }
4435
4436 fn print_module_recursive(&mut self, module_id: Id) {
4438 if module_id != self.krate.root && !self.selected_ids.contains(&module_id) {
4440 return;
4441 }
4442
4443 if let Some(item) = self.krate.index.get(&module_id) {
4444 let module_segment = item.name.as_deref().unwrap_or("").to_string();
4446 if module_id == self.krate.root {
4447 self.current_module_path = vec![
4449 self.krate
4450 .index
4451 .get(&self.krate.root)
4452 .unwrap()
4453 .name
4454 .as_ref()
4455 .unwrap()
4456 .replace('-', "_"),
4457 ];
4458 } else {
4459 self.current_module_path.push(module_segment);
4460 }
4461
4462 let module_header_level = self.get_current_header_level(); let header_prefix = self.get_header_prefix();
4464 let module_path_str = self.current_module_path.join("::");
4465 let display_path = if module_path_str.is_empty() {
4466 item.name.as_deref().unwrap_or("::")
4467 } else {
4468 &module_path_str
4469 };
4470
4471 writeln!(
4473 self.output,
4474 "\n{} {} Module: `{}`\n", "#".repeat(module_header_level),
4476 header_prefix,
4477 display_path
4478 )
4479 .unwrap();
4480
4481 self.printed_ids
4484 .entry(module_id)
4485 .or_insert_with(|| header_prefix.clone());
4486
4487 self.push_level();
4488
4489 self.print_docs(item);
4491
4492 if !self.no_common_traits {
4494 let mod_common = self.calculate_module_common_traits(&module_id);
4495 self.module_common_traits
4496 .insert(module_id, mod_common.clone()); let displayable_module_common: Vec<FormattedTraitImpl> = mod_common
4499 .iter()
4500 .filter(|nt| !self.crate_common_traits.contains(nt)) .cloned()
4502 .collect();
4503
4504 if !displayable_module_common.is_empty() {
4505 let common_traits_header_level = self.get_current_header_level(); let common_traits_prefix = self.get_header_prefix();
4507 writeln!(
4508 self.output,
4509 "{} {} Common Traits\n",
4510 "#".repeat(common_traits_header_level),
4511 common_traits_prefix
4512 )
4513 .unwrap();
4514 writeln!(self.output, "In addition to the crate's 'Common Traits', the following traits are commonly implemented by types in this module. Unless otherwise noted, you can assume these traits are implemented:\n").unwrap();
4515 let formatted_list = self.format_trait_list(&displayable_module_common);
4516 if !formatted_list.is_empty() {
4517 write!(self.output, "{}", formatted_list).unwrap();
4518 }
4519 self.post_increment_current_level(); }
4521 }
4522
4523 self.print_module_contents(&module_id);
4525
4526 self.pop_level();
4527 self.post_increment_current_level();
4528
4529 if let Some(children) = self.module_tree.children.get(&module_id).cloned() {
4531 for child_id in children {
4532 self.print_module_recursive(child_id);
4533 }
4534 }
4535
4536 if module_id != self.krate.root {
4538 self.current_module_path.pop();
4539 }
4540 }
4541 }
4542
4543 fn finalize(mut self) -> String {
4545 let root_item = self.krate.index.get(&self.krate.root).unwrap(); let crate_name = root_item.name.as_deref().unwrap_or("Unknown Crate");
4547 let crate_version = self.krate.crate_version.as_deref().unwrap_or("");
4548 let crate_header_level = 1; self.doc_path.clear();
4552
4553 writeln!(
4555 self.output,
4556 "{} {} API ({})\n", "#".repeat(crate_header_level),
4558 crate_name,
4559 crate_version
4560 )
4561 .unwrap();
4562 self.push_level();
4564
4565 if let Some(desc) = &self.manifest_data.description {
4567 writeln!(self.output, "{}\n", desc).unwrap();
4568 }
4569
4570 let manifest_section_level = self.get_current_header_level(); let manifest_header_prefix = self.get_header_prefix();
4573 writeln!(
4574 self.output,
4575 "{} {} Manifest\n",
4576 "#".repeat(manifest_section_level),
4577 manifest_header_prefix
4578 )
4579 .unwrap();
4580
4581 if let Some(hp) = &self.manifest_data.homepage {
4583 writeln!(self.output, "- Homepage: <{}>", hp).unwrap();
4584 }
4585 if let Some(repo) = &self.manifest_data.repository {
4586 writeln!(self.output, "- Repository: <{}>", repo).unwrap();
4587 }
4588 if !self.manifest_data.categories.is_empty() {
4589 writeln!(
4590 self.output,
4591 "- Categories: {}",
4592 self.manifest_data.categories.join(", ")
4593 )
4594 .unwrap();
4595 }
4596 if let Some(lic) = &self.manifest_data.license {
4597 writeln!(self.output, "- License: {}", lic).unwrap();
4598 }
4599 if let Some(rv) = &self.manifest_data.rust_version {
4600 writeln!(self.output, "- rust-version: `{}`", rv).unwrap();
4601 }
4602 if let Some(ed) = &self.manifest_data.edition {
4603 writeln!(self.output, "- edition: `{}`", ed).unwrap();
4604 }
4605 writeln!(self.output).unwrap(); let features_section_level = self.get_current_header_level() + 1; self.push_level(); let features_header_prefix = self.get_header_prefix();
4611 writeln!(
4612 self.output,
4613 "{} {} Features\n",
4614 "#".repeat(features_section_level),
4615 features_header_prefix
4616 )
4617 .unwrap();
4618
4619 if self.manifest_data.features.is_empty() {
4621 writeln!(self.output, "- None").unwrap();
4622 } else {
4623 let mut sorted_features: Vec<_> = self.manifest_data.features.keys().collect();
4625 sorted_features.sort_unstable();
4626 for feature_name in sorted_features {
4627 writeln!(self.output, "- `{}`", feature_name).unwrap();
4629 }
4630 }
4631 writeln!(self.output).unwrap(); self.pop_level(); self.post_increment_current_level();
4636
4637 if let Some(extra) = &self.crate_extra
4639 && let Some(readme) = &extra.readme_content
4640 {
4641 info!("Injecting README content.");
4642 let section_level = self.get_current_header_level(); let header_prefix = self.get_header_prefix();
4644 writeln!(
4645 self.output,
4646 "\n{} {} README\n",
4647 "#".repeat(section_level),
4648 header_prefix
4649 )
4650 .unwrap();
4651 let adjusted_readme = adjust_markdown_headers(readme, section_level);
4652 writeln!(self.output, "{}\n", adjusted_readme).unwrap();
4653 self.post_increment_current_level(); }
4655
4656 if !self.no_common_traits && !self.crate_common_traits.is_empty() {
4658 let common_traits_level = self.get_current_header_level(); let common_traits_prefix = self.get_header_prefix();
4660 writeln!(
4661 self.output,
4662 "\n{} {} Common Traits\n",
4663 "#".repeat(common_traits_level),
4664 common_traits_prefix
4665 )
4666 .unwrap();
4667 writeln!(self.output, "The following traits are commonly implemented by types in this crate. Unless otherwise noted, you can assume these traits are implemented:\n").unwrap();
4668
4669 let sorted_common_traits: Vec<FormattedTraitImpl> = {
4670 let mut traits: Vec<_> = self.crate_common_traits.iter().cloned().collect();
4671 traits.sort_by_key(|t| t.formatted_markdown_list_entry.clone());
4672 traits
4673 };
4674
4675 let formatted_list = self.format_trait_list(&sorted_common_traits);
4676 if !formatted_list.is_empty() {
4677 write!(self.output, "{}", formatted_list).unwrap();
4678 }
4679 writeln!(self.output).unwrap();
4680 self.post_increment_current_level(); }
4682
4683 if let Some(resolved_root_module) = self.resolved_modules.get(&self.krate.root) {
4688 let macro_ids: Vec<Id> = resolved_root_module
4689 .items
4690 .iter()
4691 .filter(|id| self.selected_ids.contains(id))
4692 .filter(|id| {
4693 matches!(
4694 self.get_item_kind(id),
4695 Some(ItemKind::Macro | ItemKind::ProcAttribute | ItemKind::ProcDerive)
4696 )
4697 })
4698 .cloned() .collect();
4700
4701 if !macro_ids.is_empty() {
4702 let section_level = self.get_current_header_level(); let header_prefix = self.get_header_prefix();
4704 writeln!(
4705 self.output,
4706 "\n{} {} Macros",
4707 "#".repeat(section_level),
4708 header_prefix
4709 )
4710 .unwrap();
4711
4712 self.push_level(); let mut sorted_macros = macro_ids;
4714 sorted_macros
4715 .sort_by_key(|id| self.krate.index.get(id).and_then(|item| item.name.clone()));
4716 for id in sorted_macros {
4717 self.print_item_details(&id); }
4719 self.pop_level(); self.post_increment_current_level(); }
4722 }
4723
4724 self.print_module_recursive(self.krate.root);
4728
4729 let top_level_ids = self.module_tree.top_level_modules.clone();
4732 for module_id in top_level_ids {
4733 self.print_module_recursive(module_id); }
4735
4736 let mut unprinted_ids = Vec::new();
4738 for id in &self.selected_ids {
4739 if !self.printed_ids.contains_key(id) {
4740 if let Some(item) = self.krate.index.get(id) {
4744 if !matches!(
4745 item.inner,
4746 ItemEnum::Impl(_)
4747 | ItemEnum::Use { .. }
4748 | ItemEnum::StructField(_)
4749 | ItemEnum::Module(_) ) && item.name.is_some()
4751 {
4752 unprinted_ids.push(*id);
4753 }
4754 else if item.name.is_none()
4756 || matches!(item.inner, ItemEnum::StructField(_) | ItemEnum::Module(_))
4757 {
4758 self.printed_ids.insert(*id, "SKIPPED_OTHER".to_string());
4759 }
4760 } else {
4761 unprinted_ids.push(*id);
4763 }
4764 }
4765 }
4766
4767 if !unprinted_ids.is_empty() {
4768 if self.include_other {
4769 warn!(
4770 "Found {} selected items that were not printed in the main structure. Including them in the 'Other' section.",
4771 unprinted_ids.len()
4772 );
4773 let other_section_level = self.get_current_header_level(); let header_prefix = self.get_header_prefix();
4775 writeln!(
4776 self.output,
4777 "\n{} {} Other", "#".repeat(other_section_level),
4779 header_prefix
4780 )
4781 .unwrap();
4782
4783 self.push_level();
4785 unprinted_ids.sort_by_key(|id| {
4787 (
4788 self.krate.paths.get(id).map(|p| p.path.clone()),
4789 self.krate.index.get(id).and_then(|i| i.name.clone()),
4790 )
4791 });
4792
4793 for id in &unprinted_ids {
4794 let path_str = format_id_path_canonical(id, self.krate);
4795 warn!("Including unprinted item in 'Other' section: {}", path_str);
4796
4797 if let Some(item) = self.krate.index.get(id) {
4799 self.print_item_details(id);
4801
4802 if let Some(span) = &item.span {
4807 writeln!(
4808 self.output,
4809 "_Source: `{}:{}:{}`_\n", span.filename.display(),
4811 span.begin.0 + 1, span.begin.1 + 1 )
4814 .unwrap();
4815 }
4816 self.print_graph_context(id);
4818 } else {
4819 self.post_increment_current_level(); let other_item_level = self.get_current_header_level();
4822 let item_prefix = self.get_header_prefix();
4823 writeln!(
4824 self.output,
4825 "\n{} {} `{}`\n",
4826 "#".repeat(other_item_level),
4827 item_prefix,
4828 path_str )
4830 .unwrap();
4831 writeln!(self.output, "_Error: Item details not found in index._\n")
4832 .unwrap();
4833 self.print_graph_context(id); }
4835 }
4836 self.pop_level(); } else {
4838 let mut counts_by_kind: HashMap<ItemKind, usize> = HashMap::new(); for id in &unprinted_ids {
4841 if let Some(kind) = self.get_item_kind(id) {
4842 *counts_by_kind.entry(kind).or_insert(0) += 1;
4843 } else {
4844 *counts_by_kind.entry(ItemKind::StructField).or_insert(0) += 1;
4846 }
4848 }
4849 warn!(
4850 "Skipped printing {} items not fitting into standard sections (use --include-other to see them):",
4851 unprinted_ids.len()
4852 );
4853 let mut sorted_counts: Vec<_> = counts_by_kind.into_iter().collect();
4855 sorted_counts.sort_by_key(|(kind, _)| format!("{:?}", kind)); for (kind, count) in sorted_counts {
4858 warn!(" - {:?}: {}", kind, count);
4859 }
4860 }
4861 }
4862
4863 let examples_readme_content_clone = self
4866 .crate_extra
4867 .as_ref()
4868 .and_then(|extra| extra.examples_readme_content.clone());
4869 let examples_clone = self
4870 .crate_extra
4871 .as_ref()
4872 .map_or_else(Vec::new, |extra| extra.examples.clone());
4873
4874 if !examples_clone.is_empty() || examples_readme_content_clone.is_some() {
4875 let examples_section_level = self.get_current_header_level(); let header_prefix = self.get_header_prefix();
4877 writeln!(
4878 self.output,
4879 "\n{} {} Examples Appendix\n",
4880 "#".repeat(examples_section_level),
4881 header_prefix
4882 )
4883 .unwrap();
4884 self.push_level(); if let Some(readme) = examples_readme_content_clone {
4887 let adjusted_readme = adjust_markdown_headers(&readme, examples_section_level);
4888 writeln!(self.output, "{}\n", adjusted_readme).unwrap();
4889 }
4890
4891 for (filename, content) in &examples_clone {
4892 let example_header_level = self.get_current_header_level(); let example_prefix = self.get_header_prefix();
4894 writeln!(
4895 self.output,
4896 "{} {} `{}`\n",
4897 "#".repeat(example_header_level),
4898 example_prefix,
4899 filename
4900 )
4901 .unwrap();
4902 writeln!(self.output, "```rust\n{}\n```\n", content).unwrap();
4903 self.post_increment_current_level(); }
4905 self.pop_level(); self.post_increment_current_level(); }
4908 self.output
4909 }
4910}