cargo_docs_md/source/
mod.rs1use std::fs;
42use std::path::{Path, PathBuf};
43use std::result::Result;
44
45mod collector;
46mod integration;
47mod locator;
48mod parser;
49pub mod types;
50
51pub use collector::{
52 CollectOptions, CollectedCrate, CollectionResult, SourceCollector, SourceManifest,
53};
54pub use locator::SourceLocator;
55pub use parser::SourceParser;
56pub use types::{
57 ConstInfo, CrateSource, EnumInfo, FieldInfo, FunctionInfo, ImplInfo, MacroInfo, PrivateItem,
58 StaticInfo, StructInfo, TraitInfo, TypeAliasInfo, VariantInfo,
59};
60
61#[must_use]
85pub fn find_source_dir(root: &Path) -> Option<PathBuf> {
86 let entries = fs::read_dir(root).ok()?;
87
88 let mut source_dirs: Vec<PathBuf> = entries
89 .filter_map(Result::ok)
90 .map(|e| e.path())
91 .filter(|p| {
92 p.is_dir()
93 && p.file_name()
94 .and_then(|n| n.to_str())
95 .is_some_and(|n| n.starts_with(".source_"))
96 })
97 .collect();
98
99 source_dirs.sort_by(|a, b| {
102 let ts_a = extract_source_timestamp(a);
103 let ts_b = extract_source_timestamp(b);
104 ts_b.cmp(&ts_a)
105 });
106
107 source_dirs.into_iter().next()
108}
109
110fn extract_source_timestamp(path: &Path) -> u64 {
114 path.file_name()
115 .and_then(|n| n.to_str())
116 .and_then(|s| s.strip_prefix(".source_"))
117 .and_then(|s| s.parse().ok())
118 .unwrap_or(0)
119}
120
121#[cfg(test)]
122mod tests {
123 use std::fs as StdFs;
124
125 use tempfile::TempDir;
126
127 use super::*;
128
129 #[test]
130 fn find_source_dir_returns_none_for_empty_dir() {
131 let temp = TempDir::new().unwrap();
132 let result = find_source_dir(temp.path());
133 assert!(result.is_none());
134 }
135
136 #[test]
137 fn find_source_dir_finds_single_source_dir() {
138 let temp = TempDir::new().unwrap();
139 StdFs::create_dir(temp.path().join(".source_12345")).unwrap();
140
141 let result = find_source_dir(temp.path());
142 assert!(result.is_some());
143 assert!(result.unwrap().ends_with(".source_12345"));
144 }
145
146 #[test]
147 fn find_source_dir_returns_most_recent() {
148 let temp = TempDir::new().unwrap();
149 StdFs::create_dir(temp.path().join(".source_10000")).unwrap();
150 StdFs::create_dir(temp.path().join(".source_99999")).unwrap();
151 StdFs::create_dir(temp.path().join(".source_50000")).unwrap();
152
153 let result = find_source_dir(temp.path());
154 assert!(result.is_some());
155 assert!(result.unwrap().ends_with(".source_99999"));
156 }
157
158 #[test]
159 fn find_source_dir_ignores_non_source_dirs() {
160 let temp = TempDir::new().unwrap();
161 StdFs::create_dir(temp.path().join("source_12345")).unwrap(); StdFs::create_dir(temp.path().join(".sourcecode")).unwrap(); StdFs::create_dir(temp.path().join("src")).unwrap();
164
165 let result = find_source_dir(temp.path());
166 assert!(result.is_none());
167 }
168
169 #[test]
170 fn extract_source_timestamp_parses_valid() {
171 let path = PathBuf::from("/project/.source_1733660400");
172 assert_eq!(extract_source_timestamp(&path), 1_733_660_400);
173 }
174
175 #[test]
176 fn extract_source_timestamp_returns_zero_for_invalid() {
177 assert_eq!(
178 extract_source_timestamp(&PathBuf::from("/project/.source_abc")),
179 0
180 );
181 assert_eq!(
182 extract_source_timestamp(&PathBuf::from("/project/source_123")),
183 0
184 );
185 }
186}