rustdoc_markdown/
lib.rs

1#![allow(clippy::uninlined_format_args)]
2#![allow(clippy::too_many_lines)]
3#![allow(clippy::cognitive_complexity)] // Allow complex functions for now
4
5//! `rustdoc-markdown` is a tool to convert Rust crate documentation into a
6//! monolithic Markdown document, primarily designed for consumption by Large
7//! Language Models (LLMs).
8//!
9//! It achieves this by:
10//! 1. Fetching crate information from crates.io (if not using a local manifest).
11//! 2. Downloading and unpacking the crate source.
12//! 3. Running `rustdoc` to generate JSON output (`rustdoc_types::Crate`).
13//! 4. Parsing the `Cargo.toml` of the crate.
14//! 5. Optionally reading extra information like the crate's `README.md` and
15//!    source code examples from the `examples/` directory.
16//! 6. Processing the `rustdoc_types::Crate` and supplementary information.
17//! 7. Rendering a structured Markdown document.
18//!
19//! ## Key Features
20//!
21//! - **LLM-Optimized Output**: The Markdown is structured for clarity and conciseness,
22//!   aiming to provide effective context for LLMs. Header levels might exceed
23//!   standard Markdown (h6) to maintain a consistent hierarchical structure.
24//! - **Comprehensive Documentation**: Includes API documentation, README, and examples.
25//! - **"Common Traits" Summarization**: To reduce output size, frequently implemented
26//!   traits can be summarized at the crate and module levels. This can be disabled.
27//! - **Path Filtering**: Allows generation of documentation for specific modules or items.
28//! - **Template Mode**: Can output template markers instead of actual documentation,
29//!   useful for identifying missing docstrings.
30//!
31//! ## Main Entry Points
32//!
33//! - [`Printer`]: The core struct responsible for orchestrating the documentation
34//!   generation process. It uses a builder pattern for configuration.
35//! - [`run_rustdoc`]: A utility function to execute `rustdoc` and parse its JSON output.
36//! - [`CrateExtraReader`]: Reads supplementary information like READMEs and examples.
37//!
38//! ## Example Usage (Library)
39//!
40//! ```no_run
41//! use anyhow::Result;
42//! use cargo_manifest::Manifest;
43//! use rustdoc_markdown::{Printer, CrateExtraReader, run_rustdoc, cratesio};
44//! use std::path::Path;
45//! use reqwest::Client; // Add this line
46//!
47//! async fn generate_docs_for_crate(
48//!     crate_name: &str,
49//!     version_req: &str,
50//!     output_md_path: &Path,
51//!     build_dir: &Path,
52//! ) -> Result<()> {
53//!     // 1. Setup client and directories
54//!     let client = Client::new();
55//!     std::fs::create_dir_all(build_dir)?;
56//!
57//!     // 2. Find the best version on crates.io
58//!     let target_version = cratesio::find_best_version(
59//!         &client,
60//!         crate_name,
61//!         version_req,
62//!         false, // include_prerelease
63//!     )
64//!     .await?;
65//!
66//!     // 3. Download and unpack the crate
67//!     let crate_dir = cratesio::download_and_unpack_crate(
68//!         &client,
69//!         &target_version,
70//!         build_dir,
71//!     )
72//!     .await?;
73//!
74//!     // 4. Parse the crate's Cargo.toml
75//!     let manifest_path = crate_dir.join("Cargo.toml");
76//!     let manifest = Manifest::from_path(&manifest_path)?;
77//!
78//!     // 5. Run rustdoc to get rustdoc_types::Crate
79//!     let krate_data = run_rustdoc(
80//!         &crate_dir,
81//!         &target_version.crate_name,
82//!         None,    // features
83//!         false,   // no_default_features
84//!         None,    // target
85//!         true,    // allow_rustup (ensure nightly is available)
86//!     )?;
87//!
88//!     // 6. Read extra crate information (README, examples)
89//!     let extra_reader = CrateExtraReader::new(); // Default: reads README and examples
90//!     let crate_extra = extra_reader.read(&manifest, &crate_dir)?;
91//!
92//!     // 7. Configure and run the Printer
93//!     let printer = Printer::new(&manifest, &krate_data)
94//!         .crate_extra(crate_extra)
95//!         // .paths(&["::my_module".to_string()]) // Optional: filter by path
96//!         // .no_common_traits() // Optional: disable common traits summary
97//!         ;
98//!
99//!     let markdown_content = printer.print()?;
100//!
101//!     // 8. Write to output file
102//!     std::fs::write(output_md_path, markdown_content)?;
103//!
104//!     println!("Successfully generated Markdown for {} v{} at {}",
105//!              crate_name, target_version.num, output_md_path.display());
106//!
107//!     Ok(())
108//! }
109//! ```
110
111use anyhow::{Context, Result, bail}; // Add Context
112use cargo_manifest::{FeatureSet, Manifest as CargoManifest, StringOrBool}; // Renamed Manifest to CargoManifest
113use 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 HashMap instead of BTreeMap where needed
121use std::fmt::Write as FmtWrite; // Use FmtWrite alias
122use std::hash::{Hash, Hasher};
123use std::path::Path as FilePath; // Corrected use statement
124use tracing::{debug, info, trace, warn};
125// Add fs import for CrateExtraReader
126use std::fs;
127use std::io::BufReader; // Added for reading JSON file
128
129// Import pulldown-cmark related items
130use pulldown_cmark::{Event, Parser as CmarkParser, Tag, TagEnd}; // Import Tag, TagEnd
131use pulldown_cmark_to_cmark::cmark;
132
133/// The specific nightly Rust toolchain version required by this crate.
134pub const NIGHTLY_RUST_VERSION: &str = "nightly-2025-11-27";
135
136pub mod cratesio;
137
138#[doc(hidden)]
139pub mod graph;
140
141// --- CrateExtra Structures ---
142
143/// Holds extra crate information like README and examples.
144///
145/// This data is read from the crate's source directory and can be
146/// included in the generated Markdown documentation.
147#[derive(Debug, Clone, Default)]
148pub struct CrateExtra {
149    /// The content of the crate's main `README.md` or `README` file.
150    pub readme_content: Option<String>,
151    /// The content of the `README.md` or `README` file within the `examples/` directory.
152    pub examples_readme_content: Option<String>,
153    /// A list of (filename, content) tuples for Rust files found in the `examples/` directory.
154    pub examples: Vec<(String, String)>, // Vec of (filename, content)
155}
156
157/// Builder for reading [`CrateExtra`] data from a crate's source directory.
158///
159/// Allows selective reading of README files and examples.
160#[derive(Debug, Default)]
161pub struct CrateExtraReader {
162    read_readme: bool,
163    read_examples: bool,
164}
165
166impl CrateExtraReader {
167    /// Creates a new `CrateExtraReader` with default settings, which will
168    /// attempt to read the main README file and examples from the `examples/` directory.
169    pub fn new() -> Self {
170        Self {
171            read_readme: true,
172            read_examples: true,
173        }
174    }
175
176    /// Disables reading of the crate's main README file (`README.md` or `README`).
177    pub fn no_readme(mut self) -> Self {
178        self.read_readme = false;
179        self
180    }
181
182    /// Disables reading of example files from the `examples/` directory and its README.
183    pub fn no_examples(mut self) -> Self {
184        self.read_examples = false;
185        self
186    }
187
188    /// Reads the extra crate information from the specified crate source directory.
189    ///
190    /// # Arguments
191    ///
192    /// * `manifest`: A reference to the parsed `Cargo.toml` of the package.
193    /// * `package_dir`: The path to the root directory of the package.
194    ///
195    /// # Returns
196    ///
197    /// A `Result` containing the [`CrateExtra`] data, or an error if reading fails.
198    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()); // Get local value of MaybeInherited
207
208            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                    // Default to README.md if true or not specified
214                    let default_readme_md = package_dir.join("README.md");
215                    if default_readme_md.exists() {
216                        Some(default_readme_md)
217                    } else {
218                        // Fallback to README if README.md doesn't exist
219                        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                // This case means readme was not specified, and default README.md/README were not found
242                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// --- Manifest Data ---
284
285#[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, // Using cargo-manifest's FeatureSet
295}
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
335/// Runs `rustdoc` for a given crate and parses the resulting JSON output.
336///
337/// This function uses the `rustdoc-json` crate to invoke `rustdoc` with the
338/// necessary flags to produce JSON output. It requires a specific nightly
339/// Rust toolchain (see [`NIGHTLY_RUST_VERSION`]).
340///
341/// # Arguments
342///
343/// * `crate_dir`: Path to the root directory of the crate.
344/// * `crate_name`: The name of the crate (as it appears in `Cargo.toml`).
345/// * `features`: An optional space-separated string of features to enable.
346/// * `no_default_features`: If `true`, the `default` feature will not be activated.
347/// * `target`: An optional target triple to build documentation for.
348/// * `allow_rustup`: If `true`, the function will attempt to install the required
349///   nightly toolchain using `rustup`. If `false` and the toolchain is not
350///   present, it may fail.
351///
352/// # Returns
353///
354/// A `Result` containing the parsed [`rustdoc_types::Crate`] data, or an error
355/// if `rustdoc` execution or JSON parsing fails.
356pub 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        // Install the required nightly toolchain
374        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) // Specify the nightly toolchain
387        .target_dir(crate_dir.join("target")) // Set the output directory
388        .package(crate_name); // Specify the package
389
390    // Apply feature flags
391    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    // Apply target
405    if let Some(target_str) = target {
406        info!("Setting target: {}", target_str);
407        builder = builder.target(target_str.to_string());
408    }
409
410    // Generate the JSON file
411    match builder.build() {
412        Ok(s) => {
413            info!("Generated rustdoc JSON at: {}", s.display());
414        }
415        Err(e) => {
416            // Attempt to read stderr if possible (rustdoc-json might not expose it easily)
417            eprintln!("--- rustdoc-json build failed ---");
418            eprintln!("{:?}", e); // Print the error itself
419
420            // Try to read potential rustdoc output if the file exists but is invalid
421            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
449/// Gets the `Id` associated with a type, if it's a path-based type.
450pub(crate) fn get_type_id(ty: &Type) -> Option<Id> {
451    match ty {
452        Type::ResolvedPath(p) => Some(p.id),
453        Type::Generic(_) => None, // Generic types don't have a direct ID in this context
454        Type::Primitive(_) => None,
455        Type::FunctionPointer(_) => None, // Function pointers don't have an ID
456        Type::Tuple(_) => None,
457        Type::Slice(inner) => get_type_id(inner), // Look inside
458        Type::Array { type_, .. } => get_type_id(type_), // Look inside
459        Type::Pat { type_, .. } => get_type_id(type_), // Look inside
460        Type::Infer => None,
461        Type::RawPointer { type_, .. } => get_type_id(type_), // Look inside
462        Type::BorrowedRef { type_, .. } => get_type_id(type_), // Look inside
463        Type::QualifiedPath { self_type, .. } => get_type_id(self_type), // Focus on self_type for impl matching
464        Type::ImplTrait(_) => None,
465        Type::DynTrait(_) => None,
466    }
467}
468
469// --- Formatting Helpers ---
470
471/// Formats a list of attributes, filtering out derive attributes.
472/// Each attribute is on a new line.
473/// Returns a string like `#[attr1]\n#[attr2]\n` (with a trailing newline if not empty).
474/// Convert an Attribute enum to a string representation
475fn 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
515/// Formats a list of attributes, filtering out derive attributes, for inline display.
516/// Returns a string like `#[attr1] #[attr2] ` (with a trailing space if not empty).
517fn 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
531/// Helper to check if an item has non-empty documentation.
532fn has_docs(item: &Item) -> bool {
533    item.docs.as_ref().is_some_and(|d| !d.trim().is_empty())
534}
535
536/// Adjusts the markdown header levels in a string using pulldown-cmark.
537/// Increases the level of each header (e.g., `#` -> `###`) based on the base level.
538/// Caps the maximum level at 6 (`######`).
539fn 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            // Explicitly match on HeadingLevel variants to get usize
549            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            // Explicitly match on HeadingLevel variants to get usize
569            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); // Pre-allocate slightly
586    cmark(transformed_events, &mut out_buf).expect("Markdown formatting failed");
587    out_buf
588}
589
590/// Indents each line of a string by the specified amount.
591fn 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
599/// Cleans common prefixes like `core::marker::`, `core::ops::`, `alloc::`, `std::` from a path string.
600fn clean_trait_path(path_str: &str) -> String {
601    path_str
602        .replace("core::marker::", "")
603        .replace("core::ops::", "") // Add common core paths
604        .replace("core::fmt::", "")
605        .replace("core::cmp::", "")
606        .replace("core::clone::", "")
607        .replace("core::hash::", "")
608        .replace("core::panic::unwind_safe::", "") // For UnwindSafe/RefUnwindSafe
609        // Keep core::option::Option ? Maybe not needed as often in where clauses.
610        .replace("core::", "") // General core removal last
611        .replace("alloc::string::", "") // Clean alloc paths too
612        .replace("alloc::vec::", "")
613        .replace("alloc::boxed::", "")
614        .replace("alloc::borrow::", "") // For Borrow/BorrowMut/ToOwned
615        .replace("alloc::", "") // General alloc removal
616        .replace("std::", "") // Also clean std paths potentially used via prelude
617}
618
619/// Formats the canonical path to an item ID, using its path from krate.paths.
620fn 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            // Fallback if not in paths (e.g., some external or generated IDs)
627            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
635/// Formats a Path struct, trying to use the canonical path for the ID.
636fn format_path(path: &Path, krate: &Crate) -> String {
637    // Use the canonical path if available, otherwise use the path string in the struct
638    let base_path = format_id_path_canonical(&path.id, krate);
639
640    let cleaned_base_path = clean_trait_path(&base_path); // Clean the base path
641    // Use as_ref() to get Option<&GenericArgs> from Option<Box<GenericArgs>>
642    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) // Use cleaned path
646        } else {
647            cleaned_base_path // Use cleaned path
648        }
649    } else {
650        cleaned_base_path // Use cleaned path
651    }
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)) // Format full param def
664                .collect::<Vec<_>>()
665                .join(", ")
666        )
667    };
668    format!("{}{}", hrtb, format_path(&poly_trait.trait_, krate)) // Use format_path for the Path struct
669}
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)) // Add quote for lifetime
679                .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) // Use Debug for Abi for now
707            } 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)) // Ignore name pattern for now
720                    .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            // Special case for empty tuple
731            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)), // Placeholder
747        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)) // Add quote
774                .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)) // Use format_path
788                .unwrap_or("_".to_string());
789            // Args here are for the associated type, not the trait bound
790            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    // Prefer `value` if present and different, otherwise use `expr`
884    if let Some(v) = &constant.value
885        && v != &constant.expr
886    {
887        return format!("{} /* = {} */", constant.expr, v);
888    }
889    constant.expr.clone()
890}
891
892/// Formats a discriminant expression, potentially showing the value if different.
893fn 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(), // Add quote
904        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_,         // Path struct
914            generic_params, // HRTBs
915            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)) // Format full param def
926                        .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 ", // Note the space
934            };
935            format!("{}{}{}", hrtb, mod_str, format_path(trait_, krate)) // Use format_path
936        }
937        GenericBound::Outlives(lifetime) => lifetime.to_string(), // Add quote
938        GenericBound::Use(args) => {
939            // use<'a, T> syntax
940            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(), // Use string name directly
946                    })
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(), // Add quote
964        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
1006// Formats generics like <T: Bound> where T: OtherBound
1007fn 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        // Add newline and indent if params were also present and where clause is multiline
1034        if !params_str.is_empty() && where_clause.contains('\n') {
1035            write!(s, "\n  {}", where_clause).unwrap();
1036        } else {
1037            write!(s, " {}", where_clause).unwrap(); // Append single line where clause or first line of multiline
1038        }
1039    }
1040
1041    s
1042}
1043
1044// Formats generics like <T: Bound>
1045fn 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
1059// Formats only the where clause: "where T: Bound" or multi-line
1060fn 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()) // Add quotes
1105                        .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    // Determine if multi-line formatting is needed
1116    let total_len = clauses.iter().map(|s| s.len()).sum::<usize>();
1117    let is_multiline = clauses.len() > 1 || total_len > 60; // Heuristic for multi-line
1118
1119    if is_multiline {
1120        format!("where\n    {}", clauses.join(",\n    ")) // Indent contents
1121    } else {
1122        format!("where {}", clauses.join(", "))
1123    }
1124}
1125
1126// --- Structured Printing Logic ---
1127
1128/// Category of a trait implementation for display purposes.
1129#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1130enum TraitImplCategory {
1131    /// Simple, non-generic, non-blanket, non-auto impls.
1132    Simple,
1133    /// Impls with generics, associated items, or other complexities.
1134    GenericOrComplex,
1135    /// Auto traits (e.g., Send, Sync).
1136    Auto,
1137    /// Blanket implementations.
1138    Blanket,
1139}
1140
1141/// Represents a normalized trait implementation for comparison and storage.
1142///
1143/// Apart from the optional `impl_id`, this trait description is decoupled
1144/// from any specific implementation so it can be used to render common
1145/// traits for a crate or module. Unless changing the design, keep this
1146/// comment, otherwise someone will make the mistake of adding implementation
1147/// state to this struct.
1148#[derive(Debug, Clone)]
1149struct FormattedTraitImpl {
1150    trait_id: Id,
1151    /// Generics of the trait path itself (e.g., `<'a>` in `Trait<'a>`).
1152    /// This `Generics` is from `rustdoc_types` and its internal `CowStr` will have lifetime 'a.
1153    trait_generics: Generics,
1154    is_unsafe_impl: bool,
1155    is_negative: bool,
1156    /// The category this trait implementation falls into.
1157    category: TraitImplCategory,
1158    /// The pre-formatted Markdown list entry for this trait.
1159    formatted_markdown_list_entry: String,
1160    /// Optionally link back to the real Impl item in the krate index.
1161    /// This is useful when representing implementation details for a specific
1162    /// type, as opposed to common trait implementations for a crate / module.
1163    /// This link helps track what items have been printed so far.
1164    impl_id: Option<Id>,
1165}
1166
1167impl PartialEq for FormattedTraitImpl {
1168    /// Compares two FormattedTraitImpl instances for equality.
1169    /// For common trait identification, `impl_id` and `formatted_markdown_list_entry` are ignored.
1170    fn eq(&self, other: &Self) -> bool {
1171        self.trait_id == other.trait_id
1172            && self.trait_generics == other.trait_generics // Compare trait generics structure
1173            && self.is_unsafe_impl == other.is_unsafe_impl
1174            && self.is_negative == other.is_negative
1175            && self.category == other.category // Compare category
1176    }
1177}
1178impl Eq for FormattedTraitImpl {}
1179
1180impl Hash for FormattedTraitImpl {
1181    /// Hashes the FormattedTraitImpl instance.
1182    /// For common trait identification, `impl_id` and `formatted_markdown_list_entry` are ignored.
1183    fn hash<H: Hasher>(&self, state: &mut H) {
1184        self.trait_id.hash(state);
1185        self.trait_generics.hash(state); // Hash trait generics structure
1186        self.is_unsafe_impl.hash(state);
1187        self.is_negative.hash(state);
1188        self.category.hash(state); // Hash category
1189    }
1190}
1191
1192// Helper function to convert GenericArgs to Generics
1193// This function attempts to create a Generics struct from GenericArgs.
1194// It's a simplification, primarily for representing the generics *of a path* (like a trait path).
1195fn 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(); // Not typically part of GenericArgs directly
1198
1199    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), // Fallback to formatted type if not simple generic.
1210                    };
1211                    params.push(GenericParamDef {
1212                        name,
1213                        kind: rustdoc_types::GenericParamDefKind::Type {
1214                            bounds: vec![], // Bounds are in `constraints` or `where_predicates`
1215                            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, // Type info might be lost here or in `c.type_`
1231                            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        // Convert AssocItemConstraints to WherePredicates (simplified)
1248        for constraint in constraints {
1249            match constraint {
1250                rustdoc_types::AssocItemConstraint {
1251                    name: assoc_name,
1252                    args: assoc_args, // GenericArgs for the associated type itself
1253                    binding: rustdoc_types::AssocItemConstraintKind::Equality(term),
1254                } => {
1255                    // Construct a Type for the LHS: Self::AssocName<Args>
1256                    let lhs_type = Type::QualifiedPath {
1257                        name: assoc_name,
1258                        args: assoc_args.clone(),
1259                        self_type: Box::new(Type::Generic("Self".to_string())), // Placeholder "Self"
1260                        trait_: None, // Assuming it's an associated type on "Self"
1261                    };
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![], // HRTBs not directly in constraints
1282                    });
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
1316/// Checks if an `Impl` is a "passthrough generic" implementation.
1317/// This means the `impl` block's generics directly mirror the generics of the type it's for,
1318/// and the `impl` doesn't introduce its own `where` clauses or complex associated type bindings
1319/// on the trait path itself.
1320fn is_passthrough_generic_impl_check(imp: &Impl, trait_path: &Path, krate: &Crate) -> bool {
1321    // Trait path must have no args or empty angle bracketed args/constraints
1322    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    // Impl must not have associated items (types/consts)
1330    if trait_impl_has_associated_items(imp, krate) {
1331        return false;
1332    }
1333
1334    // The `for_` type must be a ResolvedPath
1335    let Type::ResolvedPath(for_path) = &imp.for_ else {
1336        return false;
1337    };
1338
1339    // The `for_` type item must exist and have generics
1340    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    // The `for_` path must have AngleBracketed args
1348    let Some(for_args_box) = &for_path.args else {
1349        // If for_path has no args, then impl must also have no params for passthrough
1350        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    // Length of generics params must match
1357    if for_generics.params.len() != for_args.len()
1358        || for_generics.params.len() != imp.generics.params.len()
1359    {
1360        return false;
1361    }
1362
1363    // Impl's where predicates must match the for_type's where predicates
1364    if for_generics.where_predicates != imp.generics.where_predicates {
1365        return false;
1366    }
1367
1368    // Each generic argument in `for_path` must correspond to a generic parameter
1369    // in `imp.generics` with matching kinds and bounds.
1370    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, // For_arg is not a simple generic param
1378        };
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; // Impl does not have a corresponding generic param
1385        };
1386
1387        // Compare kinds and bounds (simplified check)
1388        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                // Basic check, could compare types if necessary
1419            }
1420            _ => return false, // Mismatched kinds
1421        }
1422    }
1423
1424    // If all checks pass, it's a passthrough generic impl
1425    true
1426}
1427
1428impl FormattedTraitImpl {
1429    /// Creates a FormattedTraitImpl from a rustdoc_types::Impl and the krate context.
1430    fn from_impl(
1431        imp: &Impl,
1432        impl_id: Option<Id>,
1433        trait_path: &Path,
1434        krate: &Crate,
1435        printer: &Printer, // Pass printer for generate_impl_trait_block
1436    ) -> 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(); // Keep trailing newline from indent
1485                    } 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(); // Keep trailing newline
1508                    }
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(), // Trim trailing newline for consistency
1522            impl_id,
1523        }
1524    }
1525
1526    /// Retrieves the `Impl` struct from the crate index if `impl_id` is Some.
1527    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    /// Checks if the trait (referenced by trait_id) has any associated types or consts.
1540    #[allow(dead_code)] // Potentially useful later
1541    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
1560/// Generates the primary declaration string for an item (e.g., `struct Foo`, `fn bar()`).
1561/// For functions, this is deliberately simplified (no attrs, no where clause).
1562/// For traits, structs, and enums, prepends the current module path.
1563fn 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}", // Special case for unnamed fields
1566        _ => "{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, // Use fully qualified path
1620                format_generics_params_only(&t.generics.params, krate)
1621            )
1622        }
1623        ItemEnum::Function(f) => {
1624            // Simplified version for the header: no where clause, but include attributes
1625            let mut code = String::new();
1626            write!(code, "{}", format_attributes_inline(&item.attrs)).unwrap(); // Add attributes
1627            write!(code, "fn {}", name).unwrap();
1628            // Include only param generics here
1629            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))) // Use arg name from tuple
1641                .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), // Type/value in code block
1664        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), // Use simple name for items within modules
1676        ItemEnum::ExternCrate {
1677            name: crate_name, ..
1678        } => format!("extern crate {}", crate_name),
1679        ItemEnum::Use(_) => format!("use {}", name), // Basic format for Use items
1680        ItemEnum::ExternType => format!("extern type {}", name),
1681        ItemEnum::Variant(v) => format_variant_signature(item, v, krate), // Use helper
1682        ItemEnum::StructField(_) => name.to_string(), // Field name only for header
1683        ItemEnum::AssocConst { .. } => format!("const {}", name),
1684        ItemEnum::AssocType { .. } => format!("type {}", name),
1685        ItemEnum::Impl(_) => "impl".to_string(), // Impls handled specially
1686    }
1687}
1688
1689/// Generates the `struct { ... }` code block.
1690fn 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), // Use multi-line attributes
1700        name
1701    )
1702    .unwrap();
1703    // Use full generics here, including where clause
1704    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            // fields_stripped ignored
1711            if where_is_multiline {
1712                write!(code, " {{").unwrap(); // Open brace on same line as multiline where
1713            } else {
1714                write!(code, " {{").unwrap(); // Open brace on same line as generics or no generics
1715            }
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), // Use multi-line attributes
1729                        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            // fields_stripped ignored
1742            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), // Use multi-line attributes
1754                                    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            // Add semicolon only if where clause didn't add one implicitly via multiline format
1765            if !where_is_multiline {
1766                write!(code, ";").unwrap();
1767            }
1768        }
1769        StructKind::Unit => {
1770            // Add semicolon only if where clause didn't add one implicitly
1771            if !where_is_multiline {
1772                write!(code, ";").unwrap();
1773            }
1774        }
1775    }
1776    code
1777}
1778
1779/// Generates the `enum { ... }` code block.
1780fn 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), // Use multi-line attributes
1787        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) // Pass variant_item
1805            )
1806            .unwrap();
1807            // Add discriminant if present
1808            if let Some(discr) = &variant_data.discriminant {
1809                // Use format_discriminant_expr for discriminant
1810                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
1822/// Generates the `union { ... }` code block.
1823fn 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), // Use multi-line attributes
1830        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), // Use multi-line attributes
1849                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
1862/// Generates the full trait declaration code block.
1863fn 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(); // Use multi-line attributes
1868
1869    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        // Add pub if not auto or unsafe (which imply pub sometimes)
1876        write!(code, "pub ").unwrap();
1877    }
1878    write!(code, "trait {}", name).unwrap();
1879    // Add generics params and supertraits (bounds)
1880    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    // Add where clause
1899    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(); // Multiline where
1903        } else {
1904            write!(code, " {}", where_clause).unwrap(); // Single line where
1905        }
1906    }
1907
1908    // Body
1909    if t.items.is_empty() {
1910        write!(code, " {{}}").unwrap();
1911    } else {
1912        if where_clause.contains('\n') {
1913            write!(code, " {{").unwrap(); // Open brace on same line as multiline where
1914        } else {
1915            write!(code, " {{").unwrap(); // Open brace on same line as signature
1916        }
1917        writeln!(code).unwrap();
1918
1919        // Print associated items (simple versions)
1920        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), // Use multi-line attributes
1928                            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(); // Use raw default string
1934                        } 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), // Use multi-line attributes
1944                            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                        // Print simple function signature within trait def
1968                        writeln!(
1969                            code,
1970                            "    {};",
1971                            generate_function_code_block(assoc_item, f, krate)
1972                        )
1973                        .unwrap();
1974                    }
1975                    _ => {} // Ignore others
1976                }
1977            }
1978        }
1979        if !code.ends_with('\n') {
1980            writeln!(code).unwrap();
1981        }
1982        write!(code, "}}").unwrap();
1983    }
1984    code
1985}
1986
1987/// Helper to format an impl block or trait impl declaration line.
1988fn 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    // Add generics params <...>
1996    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    // Add Trait for Type
2002    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    // Add where clause
2008    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(); // Multiline where
2012        } else {
2013            write!(decl, " {}", where_clause).unwrap(); // Single line where
2014        }
2015    }
2016    decl
2017}
2018
2019/// Helper to format only the header part of an impl declaration (e.g., `impl MyTrait for MyStruct<T>`)
2020fn 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    // Add generics params <...> to the impl block itself (not the trait part)
2028    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    // Add Trait (if it's a trait impl)
2034    if let Some(trait_path) = &imp.trait_ {
2035        // For trait impl header, format trait_path with its own generics
2036        write!(decl, " {} for", format_path(trait_path, krate)).unwrap();
2037    }
2038
2039    // Add Type it's for
2040    write!(decl, " {}", format_type(&imp.for_, krate)).unwrap();
2041
2042    // DO NOT add where clause here
2043    decl
2044}
2045
2046/// Generates the full code block string for a trait impl, including associated items.
2047/// Returns None if the impl block was already printed or is effectively empty.
2048/// Skips methods within the impl block.
2049fn 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    // Note: we assume that the caller already checked that the given Impl is selected
2058    // for printing so we don't also check the individual items.
2059    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), // Use multi-line attributes
2068                        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), // Use multi-line attributes
2085                        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
2126/// Generates the full function signature for a code block.
2127fn 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    // Attributes/Keywords
2132    write!(code, "{}", format_attributes_inline(&item.attrs)).unwrap(); // Use inline attributes
2133    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(); // Use Debug for Abi
2145    }
2146
2147    // Core signature
2148    write!(code, "fn {}", name).unwrap();
2149    // Include full generics here, including where clause
2150    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    // Parameters
2155    write!(code, "(").unwrap();
2156    let args_str = f
2157        .sig
2158        .inputs
2159        .iter()
2160        .map(|(n, t)| format!("{}: {}", n, format_type(t, krate))) // Use name from tuple
2161        .collect::<Vec<_>>()
2162        .join(", ");
2163    write!(code, "{}", args_str).unwrap();
2164    if f.sig.is_c_variadic {
2165        // Correctly write to the 'code' buffer
2166        write!(code, ", ...").unwrap();
2167    }
2168    write!(code, ")").unwrap();
2169
2170    // Return type
2171    if let Some(output_type) = &f.sig.output {
2172        write!(code, " -> {}", format_type(output_type, krate)).unwrap();
2173    }
2174
2175    // Add semicolon or body indicator based on if it has implementation
2176    if f.has_body {
2177        if where_is_multiline {
2178            write!(code, " {{ ... }}").unwrap(); // Body on same line as multiline where
2179        } else {
2180            write!(code, " {{ ... }}").unwrap(); // Body on same line
2181        }
2182    } else if !where_is_multiline {
2183        // Add semicolon if it's just a declaration and doesn't already end with one (e.g., from multiline where clause)
2184        write!(code, ";").unwrap();
2185    }
2186
2187    code
2188}
2189
2190/// Formats a single enum variant's definition for the code block.
2191fn 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); // Use multi-line attributes
2194    match &v.kind {
2195        VariantKind::Plain => format!("{}{}", attrs_str, name),
2196        VariantKind::Tuple(fields) => {
2197            // fields_stripped ignored
2198            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                                    "{}{}",                                      // No pub for tuple variant fields
2208                                    format_attributes_inline(&field_item.attrs), // Use multi-line attributes
2209                                    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            // fields_stripped ignored
2221            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                                "{}{}: {}",                                  // No pub for struct variant fields
2229                                format_attributes_inline(&field_item.attrs), // Use multi-line attributes
2230                                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
2244/// Formats an enum variant's signature for the `#####` header.
2245fn format_variant_signature(item: &Item, v: &Variant, krate: &Crate) -> String {
2246    // Similar to definition but potentially simpler, without pub, maybe add discriminant visually
2247    // Attributes are NOT included in the Hx header for variants.
2248    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)) // No attributes here
2261                            } 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                        // No attributes here
2278                        } 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        // Use format_discriminant_expr
2290        write!(sig, " = {}", format_discriminant_expr(discr)).unwrap();
2291    }
2292    sig
2293}
2294
2295/// Represents the module hierarchy.
2296#[derive(Debug, Default, Clone)] // Added Clone derive
2297struct ModuleTree {
2298    /// Maps a module ID to its direct submodule IDs.
2299    children: HashMap<Id, Vec<Id>>,
2300    /// Stores the IDs of all known modules.
2301    all_modules: HashSet<Id>,
2302    /// Stores the IDs of top-level modules (excluding crate root).
2303    top_level_modules: Vec<Id>,
2304}
2305
2306/// `Printer` is responsible for generating Markdown documentation from a [`rustdoc_types::Crate`].
2307///
2308/// It uses a builder pattern for configuration. The typical workflow is:
2309///
2310/// 1. Create a `Printer` with `Printer::new(&manifest, &krate)`.
2311/// 2. Configure it using builder methods like [`paths()`](Printer::paths),
2312///    [`crate_extra()`](Printer::crate_extra), etc.
2313/// 3. Call [`print()`](Printer::print) to generate the Markdown string.
2314///
2315/// ## Features
2316///
2317/// - **Path Filtering**: Use [`paths()`](Printer::paths) to specify which items
2318///   (and their dependencies) should be included in the documentation.
2319/// - **README and Examples**: Include the crate's README and examples using
2320///   [`crate_extra()`](Printer::crate_extra) with data from [`CrateExtraReader`].
2321/// - **"Other" Items**: Control the inclusion of items not fitting standard categories
2322///   with [`include_other()`](Printer::include_other).
2323/// - **Template Mode**: Generate template markers instead of documentation content
2324///   using [`template_mode()`](Printer::template_mode), useful for identifying
2325///   missing documentation.
2326/// - **Common Traits Summarization**: By default, traits frequently implemented by types
2327///   are summarized. This can be disabled with [`no_common_traits()`](Printer::no_common_traits).
2328///
2329/// ## Example
2330///
2331/// For a complete example of using `Printer` along with other parts of this crate
2332/// to generate documentation, see the [crate-level documentation](crate).
2333pub struct Printer<'a> {
2334    krate: &'a Crate,
2335    manifest_data: CrateManifestData,
2336    // Builder options
2337    paths: Vec<String>,
2338    crate_extra: Option<CrateExtra>,
2339    include_other: bool,
2340    template_mode: bool,
2341    no_common_traits: bool,
2342    // Internal state
2343    selected_ids: HashSet<Id>,
2344    resolved_modules: HashMap<Id, ResolvedModule>,
2345    graph: IdGraph,
2346    printed_ids: HashMap<Id, String>, // Stores ID and the header prefix where it was first printed
2347    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    /// Creates a new `Printer` instance.
2358    ///
2359    /// # Arguments
2360    ///
2361    /// * `manifest`: The parsed `Cargo.toml` data for the crate, as a [`cargo_manifest::Manifest`].
2362    /// * `krate`: The [`rustdoc_types::Crate`] data produced by `rustdoc`.
2363    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(), // Will be populated by print()
2373            resolved_modules: HashMap::new(), // Will be populated by print()
2374            graph: IdGraph::default(),    // Will be populated by print()
2375            printed_ids: HashMap::new(),  // Changed to HashMap
2376            output: String::new(),
2377            module_tree: Self::build_module_tree(krate), // Initial build based on krate
2378            doc_path: Vec::new(),
2379            current_module_path: vec![],
2380            crate_common_traits: HashSet::new(), // Will be populated by print()
2381            all_type_ids_with_impls: HashSet::new(), // Will be populated by print()
2382            module_common_traits: HashMap::new(), // Will be populated during printing
2383        }
2384    }
2385
2386    /// Sets the item path filters for documentation generation.
2387    ///
2388    /// Items matching these paths (and their dependencies) will be included.
2389    ///
2390    /// ## Path Matching Rules:
2391    ///
2392    /// - Paths starting with `::` (e.g., `::my_module::MyStruct`) are treated as absolute
2393    ///   within the current crate.
2394    /// - Paths without `::` (e.g., `my_module::MyStruct` or `MyStruct`) are assumed to be
2395    ///   relative to the crate root and will be prefixed with the crate name (e.g.,
2396    ///   `crate_name::my_module::MyStruct`).
2397    /// - Matches are prefix-based. For example, `"::style"` will match `"::style::TextStyle"`
2398    ///   and `"::style::Color"`.
2399    ///
2400    /// If no paths are provided (the default), all items in the crate are considered
2401    /// for selection.
2402    pub fn paths(mut self, paths: &[String]) -> Self {
2403        self.paths = paths.to_vec();
2404        self
2405    }
2406
2407    /// Adds [`CrateExtra`] data (README, examples) to be included in the documentation.
2408    ///
2409    /// Use [`CrateExtraReader`] to obtain the `CrateExtra` instance.
2410    pub fn crate_extra(mut self, extra: CrateExtra) -> Self {
2411        self.crate_extra = Some(extra);
2412        self
2413    }
2414
2415    /// Includes items that don't fit standard categories in a final "Other" section.
2416    ///
2417    /// By default, such items (e.g., unprinted selected items that are not modules,
2418    /// impls, or struct fields) are logged as warnings and not included in the output.
2419    /// If this method is called, these items will appear at the end of the documentation,
2420    /// potentially with their source location and dependency graph context.
2421    pub fn include_other(mut self) -> Self {
2422        self.include_other = true;
2423        self
2424    }
2425
2426    /// Enables template mode for documentation output.
2427    ///
2428    /// In template mode, instead of the actual documentation content for an item,
2429    /// Mustache-like markers (e.g., `{{MISSING_DOCS_1_2_1}}`) are inserted.
2430    /// This is useful for identifying where documentation is present or missing
2431    /// in the source crate when generating documentation for LLM training or analysis.
2432    ///
2433    /// The default is `false` (template mode disabled).
2434    pub fn template_mode(mut self) -> Self {
2435        self.template_mode = true;
2436        self
2437    }
2438
2439    /// Disables the "Common Traits" summarization sections.
2440    ///
2441    /// By default, traits that are frequently implemented by types within the crate
2442    /// or specific modules are summarized in "Common Traits" sections at the crate
2443    /// and module levels. This helps to reduce redundancy in the documentation.
2444    ///
2445    /// If this method is called, these summary sections are omitted, and all
2446    /// implemented traits for each item will be listed directly with that item's
2447    /// documentation.
2448    pub fn no_common_traits(mut self) -> Self {
2449        self.no_common_traits = true;
2450        self
2451    }
2452
2453    /// Generates the Markdown documentation based on the configured options.
2454    ///
2455    /// This method consumes the `Printer` and returns the generated Markdown as a `String`.
2456    /// It performs several steps:
2457    /// 1. Resolves module items (handling `use` statements).
2458    /// 2. Selects items based on path filters and builds a dependency graph.
2459    /// 3. Calculates common traits for the crate.
2460    /// 4. Prints the crate header, README (if any), and common traits.
2461    /// 5. Recursively prints modules and their contents.
2462    /// 6. Prints any remaining "other" items if configured.
2463    /// 7. Appends examples if configured.
2464    ///
2465    /// # Returns
2466    ///
2467    /// A `Result` containing the generated Markdown `String`, or an error if
2468    /// any step fails.
2469    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, // Pass reference directly
2492            self.no_common_traits,
2493            &self, // Pass self for FormattedTraitImpl::from_impl
2494        );
2495        self.crate_common_traits = crate_common_traits;
2496        self.all_type_ids_with_impls = all_type_ids_with_impls;
2497
2498        // The finalize method consumes self and returns the String
2499        Ok(self.finalize())
2500    }
2501
2502    /// Pre-calculates common traits for the entire crate.
2503    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        // Trait Path ID -> (FormattedTraitImpl -> Count)
2523        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            // Check for mixed positive/negative implementations
2577            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                // Only one format for this trait path
2596                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                // Multiple formats, check if "Simple" is predominant
2605                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                    // Simple format is implemented by >50% of *this trait's* implementors
2612                    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    /// Calculates common traits for a specific module.
2634    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    /// Builds the module hierarchy tree.
2779    fn build_module_tree(krate: &'a Crate) -> ModuleTree {
2780        let mut tree = ModuleTree::default();
2781        let mut parent_map: HashMap<Id, Id> = HashMap::new(); // Child -> Parent
2782
2783        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                    // Sort children alphabetically by name/path here for consistent ordering within a parent
2797                    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        // Identify top-level modules (excluding crate root)
2810        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        // Sort top-level modules alphabetically by path
2817        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    /// Gets the current markdown header level based on the doc_path length.
2829    fn get_current_header_level(&self) -> usize {
2830        self.doc_path.len() + 1 // H1 if path is empty, H2 if path has one element, etc.
2831    }
2832
2833    /// Generates the header prefix string (e.g., "1.2.1:") based on the doc_path stack.
2834    /// H2 headers always get just "N:". H3+ get the full path "N.M.O:".
2835    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(); // No prefix for H1 or if stack is empty
2839        }
2840
2841        if level == 2 {
2842            // Module headers (H2) use only the last element (the H2 counter)
2843            format!("{}:", self.doc_path.last().unwrap_or(&0))
2844        } else {
2845            // H3+ headers use the full path stored in doc_path
2846            self.doc_path
2847                .iter()
2848                .map(|n| n.to_string())
2849                .collect::<Vec<_>>()
2850                .join(".")
2851                + ":"
2852        }
2853    }
2854
2855    /// Generates the template marker string (e.g., "{{MISSING_DOCS_1_2_1}}")
2856    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_{}}}}}", // Double {{ for literal {
2862                self.doc_path
2863                    .iter()
2864                    .map(|n| n.to_string())
2865                    .collect::<Vec<_>>()
2866                    .join("_")
2867            )
2868        }
2869    }
2870
2871    /// Increments the counter for the current document level.
2872    /// Note: we increment _after_ outputting something at a given level because
2873    /// we always initialize the level to 1 via `doc_path.push(1)`
2874    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    /// Pushes a new level (starting at 1) onto the document path.
2883    fn push_level(&mut self) {
2884        self.doc_path.push(1);
2885    }
2886
2887    /// Pops the last level from the document path.
2888    fn pop_level(&mut self) {
2889        self.doc_path.pop();
2890    }
2891
2892    fn get_item_kind(&self, id: &Id) -> Option<ItemKind> {
2893        // Prefer index over paths for kind, as paths might be missing for some items?
2894        self.krate
2895            .index
2896            .get(id)
2897            .map(Printer::infer_item_kind) // Use associated function syntax
2898            .or_else(|| self.krate.paths.get(id).map(|summary| summary.kind))
2899    }
2900
2901    // Fallback for inferring ItemKind if not found in paths map (should be equivalent to index anyway)
2902    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, // Keep Use kind for completeness
2907            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, // Use struct pattern
2918            ItemEnum::Static(_) => ItemKind::Static,
2919            ItemEnum::ExternType => ItemKind::ExternType, // Renamed
2920            ItemEnum::Macro(_) => ItemKind::Macro,
2921            ItemEnum::ProcMacro(ref pm) => match pm.kind {
2922                rustdoc_types::MacroKind::Bang => ItemKind::Macro, // Treat bang proc macro as Macro kind
2923                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    /// Prints the documentation string for an item, applying template mode if active.
2933    /// Header level is determined internally by the doc_path.
2934    fn print_docs(&mut self, item: &Item) {
2935        let header_level = self.get_current_header_level(); // Level of the item owning the docs
2936        match (&item.docs, self.template_mode) {
2937            // Template mode and docs exist: Print mustache marker
2938            (Some(_), true) => {
2939                let marker = self.get_template_marker();
2940                writeln!(self.output, "{}\n", marker).unwrap();
2941            }
2942            // Not template mode or no docs: Print original docs if non-empty
2943            (Some(docs), false) => {
2944                if !docs.trim().is_empty() {
2945                    // Use the new adjust_markdown_headers function
2946                    let adjusted_docs = adjust_markdown_headers(docs.trim(), header_level);
2947                    writeln!(self.output, "{}\n", adjusted_docs).unwrap();
2948                }
2949                // If docs are Some but empty, print nothing (existing behavior)
2950            }
2951            // Docs are None: Print nothing
2952            (None, _) => {}
2953        }
2954    }
2955
2956    /// Prints the details of a single selected item.
2957    /// Manages the doc_path stack for the item's header.
2958    /// Returns true if full details were printed, false if a cross-reference was printed or skipped.
2959    fn print_item_details(&mut self, id: &Id) -> bool {
2960        if !self.selected_ids.contains(id) {
2961            return false; // Skip unselected items
2962        }
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        // Skip printing details for 'Use' items, they are handled by resolution
2970        // Also skip Modules here, they are handled by the main traversal in finalize
2971        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            // Item already printed, print cross-reference instead of full details
2981            // This case is primarily for when print_item_details is called directly
2982            // (e.g., from print_items_of_kind) for an item that was already
2983            // printed via a different module path.
2984            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            // Do not push/pop level or print further details for cross-referenced item
2994            return false; // Indicate that full details were not printed
2995        }
2996
2997        // Store the prefix *before* printing details, as this is its first detailed print
2998        self.printed_ids.insert(*id, header_prefix.clone());
2999
3000        // Print Header (e.g. `### 1.1.1: `declaration``)
3001        writeln!(
3002            self.output,
3003            "\n{} {} `{}`\n", // Add newline after header
3004            "#".repeat(item_header_level),
3005            header_prefix,
3006            declaration
3007        )
3008        .unwrap();
3009
3010        self.push_level();
3011
3012        // Print Code Block for Struct/Enum/Trait/Function (if needed)
3013        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                // Check if function has attrs or where clause
3020                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(); // Check item.attrs for function attributes
3025                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 // No code block needed for simple function
3030                }
3031            }
3032            // TODO: Add code blocks for other types like TypeAlias, Constant if desired
3033            _ => 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        // Print Documentation (using the helper method)
3056        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            // Add other kinds requiring detailed sections if necessary
3064            _ => {}
3065        }
3066
3067        // Print Implementations (common to Struct, Enum, Trait, Primitive, etc.)
3068        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), // Traits list implementors
3072            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 // Full details were printed
3087    }
3088
3089    /// Checks if any selected field within a struct has documentation or if template mode is on.
3090    #[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                    // Consider it "documented" if template mode is on and docs are Some
3101                    (self.template_mode && item.docs.is_some()) || has_docs(item)
3102                })
3103        })
3104    }
3105
3106    /// Prints the "Fields" section for a struct, only if needed.
3107    /// Also marks fields without documentation as printed.
3108    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        // First pass: Mark unselected/undocumented/non-templated fields printed and check if any are printable.
3118        for field_id in &all_field_ids {
3119            if !self.selected_ids.contains(field_id) {
3120                continue; // Skip unselected fields
3121            }
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                    // Check if it's already printed to avoid double counting
3128                    if !self.printed_ids.contains_key(field_id) {
3129                        has_printable_field = true;
3130                    }
3131                } else {
3132                    // Mark non-printable field as printed immediately
3133                    self.printed_ids.insert(*field_id, self.get_header_prefix());
3134                }
3135            } else {
3136                // If item doesn't exist in index but ID was present, mark it printed to avoid issues
3137                self.printed_ids.insert(*field_id, self.get_header_prefix());
3138            }
3139        }
3140
3141        // Only print the "Fields" section if there's a printable field
3142        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", // Add newline after header
3151            "#".repeat(fields_header_level),
3152            header_prefix
3153        )
3154        .unwrap();
3155
3156        // Push a new level for the field items themselves
3157        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(); // Pop the field item level
3164
3165        self.post_increment_current_level();
3166    }
3167
3168    /// Prints the "Fields" section for a union, only if needed.
3169    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    /// Prints the details for a single struct field, only if it has printable documentation.
3220    /// Returns true if the field was printed, false otherwise.
3221    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; // Skip unselected or already printed
3224        }
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            // Only proceed if the field has printable documentation
3231            if !field_has_printable_docs {
3232                // Should already be marked printed in print_struct_fields
3233                return false;
3234            }
3235
3236            let header_prefix = self.get_header_prefix();
3237            // Mark as printed *before* printing details
3238            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                // Header: e.g., ##### 1.1.1.1: `field_name`
3245                writeln!(
3246                    self.output,
3247                    "{} {} `{}`\n", // Add newline after header
3248                    "#".repeat(field_header_level),
3249                    header_prefix,
3250                    name
3251                )
3252                .unwrap();
3253
3254                // Print docs (using helper, handles template mode)
3255                self.print_docs(item);
3256
3257                // Type (optional, could add here if needed)
3258                // writeln!(self.output, "_Type: `{}`_\n", format_type(field_type, self.krate)).unwrap();
3259                return true; // Field was printed
3260            }
3261        }
3262        // Mark as printed even if item lookup failed (shouldn't happen ideally)
3263        self.printed_ids.insert(*field_id, self.get_header_prefix());
3264        false
3265    }
3266
3267    /// Prints the details for a single enum variant field, only if it has printable documentation.
3268    /// Returns true if the field was printed, false otherwise.
3269    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; // Skip unselected or already printed
3272        }
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            // Only proceed if the field has printable documentation
3279            if !field_has_printable_docs {
3280                // If no docs, the ID should already be marked printed in print_variant_details
3281                return false;
3282            }
3283            let header_prefix = self.get_header_prefix();
3284            // Mark as printed *before* printing details
3285            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("_"); // Might be _ for tuple fields
3289                let field_header_level = self.get_current_header_level();
3290
3291                // Header: e.g., ###### 1.1.1.1.1: `field_name`
3292                // Use field index for tuple fields if name is "_" (name is often '0', '1' etc.)
3293                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", // Add newline after header
3301                    "#".repeat(field_header_level),
3302                    header_prefix,
3303                    header_name
3304                )
3305                .unwrap();
3306
3307                // Print Docs (using helper, handles template mode)
3308                self.print_docs(item);
3309
3310                // Increment level counter for this field item
3311                self.post_increment_current_level();
3312
3313                // Type (optional)
3314                // writeln!(self.output, "_Type: `{}`_\n", format_type(field_type, self.krate)).unwrap();
3315                return true; // Field was printed
3316            }
3317        }
3318        // Mark as printed even if item lookup failed
3319        self.printed_ids.insert(*field_id, self.get_header_prefix());
3320        false
3321    }
3322
3323    /// Checks if any selected variant or its fields have printable documentation.
3324    #[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                // Check variant itself
3332                if (self.template_mode && item.docs.is_some()) || has_docs(item) {
3333                    return true;
3334                }
3335                // Check fields within the variant
3336                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    /// Prints the "Variants" section for an enum, only if needed.
3359    /// Also marks variants *and their fields* without printable documentation as printed.
3360    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        // First pass: Mark non-printable variants/fields printed and check if any are printable.
3365        for variant_id in &e.variants {
3366            if !self.selected_ids.contains(variant_id) {
3367                continue; // Skip unselected variants
3368            }
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                // Check fields within the variant
3376                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                                // Mark non-printable field printed
3399                            }
3400                        } else {
3401                            // Mark unselected field id printed if present
3402                            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                    // Check if the variant itself is already printed to avoid double counting
3409                    if !self.printed_ids.contains_key(variant_id) {
3410                        has_printable_variant_or_field = true;
3411                    }
3412                } else {
3413                    // Mark non-printable variant (with no printable fields) as printed immediately
3414                    self.printed_ids
3415                        .insert(*variant_id, self.get_header_prefix());
3416                }
3417            } else {
3418                // If item doesn't exist in index but ID was present, mark it printed
3419                self.printed_ids
3420                    .insert(*variant_id, self.get_header_prefix());
3421            }
3422        }
3423
3424        // Only print the "Variants" section if there's a printable variant/field or stripped variants exist
3425        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", // Add newline after header
3434            "#".repeat(variants_header_level),
3435            header_prefix
3436        )
3437        .unwrap();
3438
3439        // Push a new level for the variant items themselves
3440        self.push_level();
3441        // Second pass: Print details for variants that have printable docs or contain printable fields
3442        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            // Add newline before stripped message only if variants were printed
3450            if printed_any_variant {
3451                writeln!(self.output).unwrap();
3452            }
3453            writeln!(self.output, "_[Private variants hidden]_").unwrap();
3454        }
3455        self.pop_level(); // Pop the variant item level
3456        self.post_increment_current_level();
3457    }
3458
3459    /// Prints the details for a single enum variant. Includes variant docs and docs for its fields if present.
3460    /// Returns true if the variant was printed (because it or its fields had printable docs), false otherwise.
3461    fn print_variant_details(&mut self, variant_id: &Id) -> bool {
3462        if !self.selected_ids.contains(variant_id) {
3463            // If already printed, do not print full details again.
3464            // Cross-referencing for variants re-exported in other modules is not typical.
3465            // If a variant is listed in a module, it's usually because its enum is listed.
3466            if self.printed_ids.contains_key(variant_id) {
3467                return false;
3468            }
3469        } else if self.printed_ids.contains_key(variant_id) {
3470            // Already printed, skip
3471            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            // Determine fields and check their printable docs
3483            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                        // Mark unselected or non-printable field printed
3504                        self.printed_ids.insert(*field_id, self.get_header_prefix());
3505                    }
3506                } else {
3507                    // Mark unselected field printed
3508                    self.printed_ids.insert(*field_id, self.get_header_prefix());
3509                }
3510            }
3511
3512            // Only print the variant if it has printable docs OR it has printable fields to print
3513            if !variant_has_printable_docs && printable_fields.is_empty() {
3514                // Mark variant as printed if skipped
3515                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            // Mark as printed *before* printing details
3522            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            // Header: e.g., ##### 1.1.1.1: `VariantSignature`
3528            writeln!(
3529                self.output,
3530                "{} {} `{}`\n", // Add newline after header
3531                "#".repeat(variant_header_level),
3532                header_prefix,
3533                signature
3534            )
3535            .unwrap();
3536            self.push_level();
3537
3538            // Print Variant Docs (using helper)
3539            self.print_docs(item);
3540
3541            // Print documented fields (if any)
3542            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", // Add newline after header
3548                    "#".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(); // Add newline before stripped message
3563                    }
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; // Variant (or its fields) was printed
3573        }
3574        // Mark as printed even if item lookup failed
3575        self.printed_ids
3576            .insert(*variant_id, self.get_header_prefix());
3577        false
3578    }
3579
3580    /// Prints the "Associated Items" section for a trait, categorized.
3581    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        // Filter and categorize selected associated items.
3588        for item_id in &t.items {
3589            if !self.selected_ids.contains(item_id) {
3590                continue;
3591            }
3592            // Mark the item as printed now, regardless of docs, to prevent it from going to "Other"
3593            // Only mark if not already printed elsewhere with a different prefix.
3594            // This is tricky because an assoc item's "primary" print location is under its trait.
3595            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                        // For now, treat associated consts similarly to required types or methods.
3619                        // Could be a separate category if needed.
3620                        // Let's put them with required types for now as they don't have 'body'.
3621                        required_types.push((*item_id, item_has_printable_docs));
3622                    }
3623                    _ => {} // Ignore others
3624                }
3625            }
3626        }
3627
3628        // If no selected associated item has printable documentation, skip printing the entire section
3629        if !has_printable_assoc_item {
3630            return;
3631        }
3632
3633        // Sort items within each category
3634        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    /// Generates the formatted summary string for an associated item (for use within impl blocks or trait defs).
3702    /// Does NOT include the markdown header. Includes docs with adjusted headers, respecting template mode.
3703    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            // Get level AFTER incrementing for the item header
3710            // let assoc_item_header_level = self.get_current_header_level(); // Level not needed here
3711
3712            // Add code block for associated functions if they have attrs/where clauses
3713            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(); // Check item.attrs for function attributes
3719                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            // Print Documentation (using helper)
3727            // Create a temporary DocPrinter to isolate output
3728            let mut temp_printer = self.clone_with_new_output();
3729            // Copy current doc path to temp printer for correct template marker generation
3730            temp_printer.doc_path = self.doc_path.clone();
3731            temp_printer.print_docs(item);
3732            write!(summary, "{}", temp_printer.output).unwrap();
3733
3734            // Potentially add default values/bounds for assoc const/type here
3735            match &item.inner {
3736                // Use correct fields { type_, value }
3737                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(); // Add newline
3741                    }
3742                }
3743                ItemEnum::AssocType { bounds, type_, .. } => {
3744                    // Use renamed field type_
3745                    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(); // Add newline
3756                    }
3757                }
3758                _ => {}
3759            }
3760            Some(summary)
3761        } else {
3762            None
3763        }
3764    }
3765
3766    /// Prints the header and summary for a single associated item (const, type, function).
3767    fn print_associated_item_summary(&mut self, assoc_item_id: &Id) {
3768        if let Some(item) = self.krate.index.get(assoc_item_id) {
3769            // Generate summary first (handles template mode internally)
3770            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                // Print Header (e.g. ##### 1.1.1.1: `declaration`)
3776                writeln!(
3777                    self.output,
3778                    "{} {} `{}`\n", // Add newline after header
3779                    "#".repeat(assoc_item_header_level),
3780                    header_prefix,
3781                    declaration
3782                )
3783                .unwrap();
3784                // Print the generated summary
3785                if !summary.trim().is_empty() {
3786                    writeln!(self.output, "{}", summary.trim()).unwrap();
3787                }
3788                writeln!(self.output).unwrap(); // Ensure a blank line afterwards
3789
3790                self.post_increment_current_level();
3791            }
3792            // If generate_associated_item_summary returns None, the item wasn't selected,
3793            // so we don't print anything, and the level increment effectively skips it.
3794        }
3795    }
3796
3797    /// Helper to categorize and format a list of FormattedTraitImpls for display.
3798    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        // Sort each category by the pre-formatted list entry string
3819        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    /// Prints Inherent and Trait Implementations *for* an item (Struct, Enum, Union, Primitive).
3865    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                // Critical: Only consider this impl if it's FOR the target_item_id
3882                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        // --- Inherent Impls ---
3889        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        // --- Trait Impls ---
3904        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; // Skip already printed impls
3909                }
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(&current_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(); // Store trait_id of common traits
3944
3945        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            // Check if this trait's path (trait_id) is among the common trait paths
3951            if module_common_traits
3952                .iter()
3953                .any(|ct| ct.trait_id == norm_trait.trait_id)
3954            {
3955                // It implements a trait that *could* be common. Remove it from missing.
3956                missing_module_common_trait_paths.remove(&norm_trait.trait_id);
3957
3958                // Now check if the *specific format* of this impl is common
3959                if !module_common_traits.contains(norm_trait) {
3960                    // The specific format is not common, so list it individually
3961                    non_common_trait_impls.push(norm_trait.clone());
3962                } else {
3963                    // The specific format *is* common, mark it printed
3964                    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                // This trait path was never common, so list this specific impl
3976                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                            // Find the corresponding FormattedTraitImpl from module_common_traits
3998                            // to get its display name (which should be the simple form)
3999                            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                                        }) // Fallback
4010                                })
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    /// Prints implementors *of* a trait. Handles template mode for the impl docs.
4034    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                    // Print where clause if it exists
4071                    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                    // Print docs for the impl block itself
4078                    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                    // Mark the impl_item ID and its associated items as printed
4084                    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    /// Prints the details of a specific impl block (header, associated items).
4102    /// Handles template mode for the impl block's docs.
4103    fn print_impl_block_details(&mut self, impl_item: &Item, imp: &Impl) {
4104        let header_prefix = self.get_header_prefix();
4105        // Mark as printed *now* before printing details
4106        if self
4107            .printed_ids
4108            .insert(impl_item.id, header_prefix.clone())
4109            .is_some()
4110        {
4111            // Already printed with a (potentially different) prefix, skip full details.
4112            // This case should ideally be caught by the caller, but as a safeguard.
4113            return;
4114        }
4115
4116        // Increment level counter for this impl block
4117        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        // Print the impl block header (e.g. #### 1.1.1: `impl ...`)
4122        writeln!(
4123            self.output,
4124            "{} {} `{}`\n", // Add newline after header
4125            "#".repeat(impl_header_level),
4126            header_prefix,      // Use the stored/current prefix
4127            impl_header.trim()  // Trim potential trailing space if no where clause added
4128        )
4129        .unwrap();
4130
4131        // Print impl block docs (using helper)
4132        // Create a temporary DocPrinter to isolate output
4133        let mut temp_printer = self.clone_with_new_output();
4134        // Copy current doc path to temp printer for correct template marker generation
4135        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        // Print associated items within this impl block
4140        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            // Important: Only process associated items that are *selected*
4145            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                    _ => {} // Should not happen in impl block
4155                }
4156            }
4157        }
4158
4159        // Push level for associated items within the impl block
4160        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(); // Pop associated item level
4188    }
4189
4190    /// Prints items of a specific kind within a given list of IDs.
4191    fn print_items_of_kind(&mut self, item_ids: &[Id], kind: ItemKind, header_name: &str) -> bool {
4192        // Filter and sort items of the target kind
4193        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; // Nothing to print for this kind
4201        }
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        // Print item details
4219        for id in items_to_print {
4220            // print_item_details now returns true if full details were printed
4221            if self.print_item_details(id) {
4222                self.post_increment_current_level();
4223            } else {
4224                // If it was a cross-reference or skipped, we still need to increment
4225                // the counter for the list item itself if we decide to print a list item.
4226                // For now, print_item_details handles the cross-ref header.
4227                // If we change print_module_contents to print list items for cross-refs,
4228                // then this post_increment might need adjustment.
4229                // For now, if print_item_details printed a cross-ref header,
4230                // it means an "item" was output, so we increment.
4231                self.post_increment_current_level();
4232            }
4233        }
4234        self.pop_level(); // Pop the item level for this section
4235
4236        true
4237    }
4238
4239    /// Prints the non-module contents of a specific module (identified by its ID).
4240    /// Uses the `resolved_modules` index to get the list of items.
4241    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(); // (Id, Declaration, Prefix)
4245
4246            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                        // Only add to cross-reference list if it's a kind we'd normally list directly
4254                        if !matches!(
4255                            item.inner,
4256                            ItemEnum::Impl(_)
4257                                | ItemEnum::Use { .. }
4258                                | ItemEnum::StructField(_)
4259                                | ItemEnum::Variant(_) // Variants are part of enums
4260                                | ItemEnum::AssocConst { .. } // Assoc items are part of traits/impls
4261                                | 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; // Skip adding to items_by_kind if already printed
4273                }
4274
4275                if let Some(kind) = self.get_item_kind(id) {
4276                    match kind {
4277                        ItemKind::Impl
4278                        | ItemKind::Variant // Handled by Enum
4279                        | ItemKind::StructField // Handled by Struct/Union/Variant
4280                        | ItemKind::AssocConst // Handled by Trait/Impl
4281                        | ItemKind::AssocType // Handled by Trait/Impl
4282                        | ItemKind::Use // Resolved, not printed directly
4283                        | ItemKind::Module => continue, // Handled by main loop
4284                        _ => {}
4285                    }
4286                    items_by_kind.entry(kind).or_default().push(*id);
4287                }
4288            }
4289
4290            // Sort items by name within each kind
4291            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            // Print cross-referenced items at the end of the module's direct items
4326            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(); // Add a blank line after the list
4345                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    /// Prints graph context for an unprinted item.
4356    fn print_graph_context(&mut self, id: &Id) {
4357        // Collect incoming edges first to release immutable borrow on self.graph
4358        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            // Sort edges for consistent output
4368            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            // Push level for this list (for template markers)
4377            self.push_level();
4378            for edge in sorted_edges {
4379                self.post_increment_current_level(); // Increment for this list item
4380                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, // Use Display impl for EdgeLabel
4397                    template_marker
4398                )
4399                .unwrap();
4400            }
4401            self.pop_level(); // Pop list level
4402            writeln!(self.output).unwrap(); // Add trailing newline
4403        } else {
4404            writeln!(
4405                self.output,
4406                "_Item has no known incoming references in the graph._\n"
4407            )
4408            .unwrap();
4409        }
4410    }
4411
4412    /// Creates a clone of the printer with an empty output buffer.
4413    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(), // Clone relevant fields
4423            resolved_modules: self.resolved_modules.clone(),
4424            graph: self.graph.clone(),
4425            printed_ids: self.printed_ids.clone(),
4426            output: String::new(), // New output buffer
4427            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    /// Recursive function to print modules and their contents depth-first.
4437    fn print_module_recursive(&mut self, module_id: Id) {
4438        // Skip if not selected. If already printed, we still need to list its re-exports.
4439        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            // Update current_module_path
4445            let module_segment = item.name.as_deref().unwrap_or("").to_string();
4446            if module_id == self.krate.root {
4447                // For root module, use the crate name
4448                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(); // Should be 2
4463            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            // Print module header (always H2)
4472            writeln!(
4473                self.output,
4474                "\n{} {} Module: `{}`\n", // Module header uses level 2
4475                "#".repeat(module_header_level),
4476                header_prefix,
4477                display_path
4478            )
4479            .unwrap();
4480
4481            // Mark module as printed only AFTER printing its header, if not already printed
4482            // This ensures the first time a module is encountered, its prefix is stored.
4483            self.printed_ids
4484                .entry(module_id)
4485                .or_insert_with(|| header_prefix.clone());
4486
4487            self.push_level();
4488
4489            // Print module docs (using helper)
4490            self.print_docs(item);
4491
4492            // --- Module Common Traits ---
4493            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()); // Store for later use
4497
4498                let displayable_module_common: Vec<FormattedTraitImpl> = mod_common
4499                    .iter()
4500                    .filter(|nt| !self.crate_common_traits.contains(nt)) // Only those not in crate common
4501                    .cloned()
4502                    .collect();
4503
4504                if !displayable_module_common.is_empty() {
4505                    let common_traits_header_level = self.get_current_header_level(); // Should be H3
4506                    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(); // Increment for this section
4520                }
4521            }
4522
4523            // Print module contents (non-module items only)
4524            self.print_module_contents(&module_id);
4525
4526            self.pop_level();
4527            self.post_increment_current_level();
4528
4529            // Recursively print child modules
4530            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            // Restore current_module_path
4537            if module_id != self.krate.root {
4538                self.current_module_path.pop();
4539            }
4540        }
4541    }
4542
4543    /// Finalizes the documentation string, printing the crate header and contents.
4544    fn finalize(mut self) -> String {
4545        let root_item = self.krate.index.get(&self.krate.root).unwrap(); // Assume root exists
4546        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; // Level 1 for crate header
4549
4550        // Clear doc path before starting
4551        self.doc_path.clear();
4552
4553        // Print Crate Header (# Crate Name (Version)) - No prefix
4554        writeln!(
4555            self.output,
4556            "{} {} API ({})\n", // Add newline after header
4557            "#".repeat(crate_header_level),
4558            crate_name,
4559            crate_version
4560        )
4561        .unwrap();
4562        // Push H2 level before starting sections/modules
4563        self.push_level();
4564
4565        // Print Crate Description (if available) - NEW
4566        if let Some(desc) = &self.manifest_data.description {
4567            writeln!(self.output, "{}\n", desc).unwrap();
4568        }
4569
4570        // Print Manifest Section (H2) - NEW
4571        let manifest_section_level = self.get_current_header_level(); // Should be 2
4572        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        // Use simple list format for manifest details
4582        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(); // Add a newline after the list
4606
4607        // Print Features Sub-section (H3) - NEW
4608        let features_section_level = self.get_current_header_level() + 1; // H3
4609        self.push_level(); // Push for the H3 features section
4610        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        // List features or state None
4620        if self.manifest_data.features.is_empty() {
4621            writeln!(self.output, "- None").unwrap();
4622        } else {
4623            // Sort features for consistent output
4624            let mut sorted_features: Vec<_> = self.manifest_data.features.keys().collect();
4625            sorted_features.sort_unstable();
4626            for feature_name in sorted_features {
4627                // TODO: Maybe show what features a feature enables? Requires more parsing.
4628                writeln!(self.output, "- `{}`", feature_name).unwrap();
4629            }
4630        }
4631        writeln!(self.output).unwrap(); // Add newline after features list
4632        self.pop_level(); // Pop H3 features level
4633
4634        // Increment H2 counter for the next section (README or Common Traits)
4635        self.post_increment_current_level();
4636
4637        // Print README content if available from CrateExtra
4638        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(); // Should be 2
4643            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(); // Increment H2 counter
4654        }
4655
4656        // Print Crate Common Traits Section (H2)
4657        if !self.no_common_traits && !self.crate_common_traits.is_empty() {
4658            let common_traits_level = self.get_current_header_level(); // Should be 2
4659            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(); // Increment H2 counter
4681        }
4682
4683        // --- Print Top-Level Sections (Macros first, then Modules) ---
4684
4685        // --- Macros Section (Level 2) ---
4686        // Find macros directly under the resolved root module
4687        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() // Clone the IDs
4699                .collect();
4700
4701            if !macro_ids.is_empty() {
4702                let section_level = self.get_current_header_level(); // Should be 2
4703                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(); // Push H3 level for macro items
4713                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); // Macro details at level 3
4718                }
4719                self.pop_level(); // Pop H3 level
4720                self.post_increment_current_level(); // Increment H2 counter
4721            }
4722        }
4723
4724        // --- Modules (Depth-First Traversal) ---
4725
4726        // 1. Print Crate Root Module explicitly (will increment H2 counter)
4727        self.print_module_recursive(self.krate.root);
4728
4729        // 2. Iterate through sorted top-level modules and print recursively
4730        // Clone the list to avoid borrowing issues
4731        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); // Will increment H2 counter
4734        }
4735
4736        // --- Handle "Other" Items ---
4737        let mut unprinted_ids = Vec::new();
4738        for id in &self.selected_ids {
4739            if !self.printed_ids.contains_key(id) {
4740                // Skip impl items and use items as they are handled implicitly or ignored
4741                // Also skip struct fields as they are handled within their containers
4742                // Also skip Modules as they are handled explicitly above
4743                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(_) // Modules explicitly skipped here
4750                    ) && item.name.is_some()
4751                    {
4752                        unprinted_ids.push(*id);
4753                    }
4754                    // If it doesn't have a name or is a StructField/Module but is selected & unprinted, mark it printed now to avoid the warning
4755                    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                    // ID selected but not in index - treat as unprinted for "Other"
4762                    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(); // Should be 2
4774                let header_prefix = self.get_header_prefix();
4775                writeln!(
4776                    self.output,
4777                    "\n{} {} Other", // Use ## level for this section
4778                    "#".repeat(other_section_level),
4779                    header_prefix
4780                )
4781                .unwrap();
4782
4783                // Push H3 level for items in Other
4784                self.push_level();
4785                // Sort unprinted items for consistent output
4786                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                    // Fetch the item to print its header and span
4798                    if let Some(item) = self.krate.index.get(id) {
4799                        // Print details (handles level incrementing internally)
4800                        self.print_item_details(id);
4801
4802                        // Get level AFTER printing details for graph context
4803                        // let item_level = self.get_current_header_level(); // Should be H3+1 = H4 // Level not needed here
4804
4805                        // Print Source Location (if available) ONLY for "Other" items
4806                        if let Some(span) = &item.span {
4807                            writeln!(
4808                                self.output,
4809                                "_Source: `{}:{}:{}`_\n", // Italic, newline after
4810                                span.filename.display(),
4811                                span.begin.0 + 1, // Line numbers are 0-based
4812                                span.begin.1 + 1  // Column numbers are 0-based
4813                            )
4814                            .unwrap();
4815                        }
4816                        // Always print graph context afterwards for items in "Other"
4817                        self.print_graph_context(id);
4818                    } else {
4819                        // Handle case where ID is selected but not in index (rare)
4820                        self.post_increment_current_level(); // Increment level for this item
4821                        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 // Use path string as header
4829                        )
4830                        .unwrap();
4831                        writeln!(self.output, "_Error: Item details not found in index._\n")
4832                            .unwrap();
4833                        self.print_graph_context(id); // Still print graph context
4834                    }
4835                }
4836                self.pop_level(); // Pop H3 level for items
4837            } else {
4838                // Group by kind and log counts
4839                let mut counts_by_kind: HashMap<ItemKind, usize> = HashMap::new(); // Use HashMap
4840                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                        // Count items where kind couldn't be determined (e.g., ID not in index)
4845                        *counts_by_kind.entry(ItemKind::StructField).or_insert(0) += 1;
4846                        // Use a placeholder kind like StructField
4847                    }
4848                }
4849                warn!(
4850                    "Skipped printing {} items not fitting into standard sections (use --include-other to see them):",
4851                    unprinted_ids.len()
4852                );
4853                // Convert HashMap to Vec for sorting before printing warnings
4854                let mut sorted_counts: Vec<_> = counts_by_kind.into_iter().collect();
4855                sorted_counts.sort_by_key(|(kind, _)| format!("{:?}", kind)); // Sort by debug representation for consistency
4856
4857                for (kind, count) in sorted_counts {
4858                    warn!("  - {:?}: {}", kind, count);
4859                }
4860            }
4861        }
4862
4863        // --- Examples Appendix ---
4864        // Clone the necessary data from self.crate_extra before the loop
4865        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(); // Should be 2
4876            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(); // Push for H3 example headers
4885
4886            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(); // Should be 3
4893                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(); // Increment H3 counter for next example
4904            }
4905            self.pop_level(); // Pop H3 example level
4906            self.post_increment_current_level(); // Increment H2 counter for next top-level section
4907        }
4908        self.output
4909    }
4910}