cargo_docs_md/multi_crate/
collection.rs

1//! Crate collection for multi-crate documentation.
2//!
3//! This module provides [`CrateCollection`], a container for multiple parsed
4//! rustdoc crates that maintains a consistent processing order.
5
6use std::collections::HashMap;
7
8use rustdoc_types::Crate;
9
10/// Collection of parsed crates ready for documentation generation.
11///
12/// Uses `HashMap` for O(1) lookups and sorts keys on-demand when iteration
13/// is needed. This is optimal for our use case where:
14/// - All crates are inserted first (parsing phase)
15/// - Sorted iteration happens later (generation phase)
16/// - Collection size is small (typically 10-50 crates)
17///
18/// # Example
19///
20/// ```ignore
21/// let mut collection = CrateCollection::new();
22/// collection.insert("tracing".to_string(), tracing_crate);
23/// collection.insert("tracing_core".to_string(), tracing_core_crate);
24///
25/// for (name, krate) in collection.iter() {
26///     println!("Processing {name}");
27/// }
28/// ```
29#[derive(Debug, Default)]
30pub struct CrateCollection {
31    /// Map from crate name to parsed Crate data.
32    /// `HashMap` provides O(1) lookups; sorting done on-demand.
33    crates: HashMap<String, Crate>,
34}
35
36impl CrateCollection {
37    /// Create an empty crate collection.
38    #[must_use]
39    pub fn new() -> Self {
40        Self::default()
41    }
42
43    /// Insert a crate into the collection.
44    ///
45    /// If a crate with the same name already exists, it is replaced
46    /// and `Some(old_crate)` is returned.
47    pub fn insert(&mut self, name: String, krate: Crate) -> Option<Crate> {
48        self.crates.insert(name, krate)
49    }
50
51    /// Get a crate by name.
52    #[must_use]
53    pub fn get(&self, name: &str) -> Option<&Crate> {
54        self.crates.get(name)
55    }
56
57    /// Get a crate by name, returning the stored key as well.
58    ///
59    /// This is useful when you need a reference to the crate name that
60    /// has the same lifetime as the collection.
61    #[must_use]
62    pub fn get_with_name(&self, name: &str) -> Option<(&str, &Crate)> {
63        self.crates
64            .get_key_value(name)
65            .map(|(k, v)| (k.as_str(), v))
66    }
67
68    /// Check if a crate exists in the collection.
69    #[must_use]
70    pub fn contains(&self, name: &str) -> bool {
71        self.crates.contains_key(name)
72    }
73
74    /// Iterate over crates in alphabetical order.
75    ///
76    /// Returns tuples of `(&crate_name, &Crate)` sorted alphabetically
77    /// by crate name for deterministic output.
78    ///
79    /// Sorting is done on-demand since collection size is small (10-50 crates).
80    pub fn iter(&self) -> impl Iterator<Item = (&String, &Crate)> {
81        let mut entries: Vec<_> = self.crates.iter().collect();
82        entries.sort_by_key(|(name, _)| *name);
83        entries.into_iter()
84    }
85
86    /// Get the number of crates in the collection.
87    #[must_use]
88    pub fn len(&self) -> usize {
89        self.crates.len()
90    }
91
92    /// Check if the collection is empty.
93    #[must_use]
94    pub fn is_empty(&self) -> bool {
95        self.crates.is_empty()
96    }
97
98    /// Get crate names in alphabetical order.
99    ///
100    /// Returns a sorted `Vec` of crate names for deterministic processing.
101    /// Sorting is done on-demand since collection size is small.
102    #[must_use]
103    pub fn names(&self) -> Vec<&String> {
104        let mut names: Vec<_> = self.crates.keys().collect();
105        names.sort();
106        names
107    }
108}
109
110#[cfg(test)]
111mod tests {
112    use super::*;
113
114    #[test]
115    fn test_insert_and_get() {
116        let collection = CrateCollection::new();
117
118        // Create a minimal mock crate (we can't easily construct a real one)
119        // In practice, these come from parsing JSON files
120        assert!(collection.is_empty());
121        assert_eq!(collection.len(), 0);
122    }
123
124    #[test]
125    fn test_names_returns_sorted() {
126        let collection = CrateCollection::new();
127        // Order should be maintained alphabetically
128        let names = collection.names();
129        let mut sorted = names.clone();
130        sorted.sort();
131        assert_eq!(names, sorted);
132    }
133}