syncdoc_migrate/
discover.rs1use proc_macro2::TokenStream;
4use std::fs;
5use std::path::{Path, PathBuf};
6use std::str::FromStr;
7use syncdoc_core::parse::ModuleContent;
8use unsynn::*;
9
10#[derive(Debug)]
12pub struct ParsedFile {
13 pub path: PathBuf,
14 pub content: ModuleContent,
15 pub original_source: String,
16}
17
18#[derive(Debug)]
20pub enum ParseError {
21 IoError(std::io::Error),
22 ParseFailed(String),
23}
24
25impl From<std::io::Error> for ParseError {
26 fn from(err: std::io::Error) -> Self {
27 ParseError::IoError(err)
28 }
29}
30
31impl std::fmt::Display for ParseError {
32 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33 match self {
34 ParseError::IoError(e) => write!(f, "IO error: {}", e),
35 ParseError::ParseFailed(msg) => write!(f, "Parse failed: {}", msg),
36 }
37 }
38}
39
40impl std::error::Error for ParseError {}
41
42#[derive(Debug)]
44pub enum ConfigError {
45 IoError(std::io::Error),
46 Other(String),
47}
48
49impl From<std::io::Error> for ConfigError {
50 fn from(err: std::io::Error) -> Self {
51 ConfigError::IoError(err)
52 }
53}
54
55impl std::fmt::Display for ConfigError {
56 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57 match self {
58 ConfigError::IoError(e) => write!(f, "IO error: {}", e),
59 ConfigError::Other(msg) => write!(f, "{}", msg),
60 }
61 }
62}
63
64impl std::error::Error for ConfigError {}
65
66pub fn discover_rust_files(source_dir: &Path) -> std::result::Result<Vec<PathBuf>, std::io::Error> {
70 let mut rust_files = Vec::new();
71 discover_rust_files_recursive(source_dir, &mut rust_files)?;
72 rust_files.sort();
73 Ok(rust_files)
74}
75
76fn discover_rust_files_recursive(
77 dir: &Path,
78 files: &mut Vec<PathBuf>,
79) -> std::result::Result<(), std::io::Error> {
80 for entry in fs::read_dir(dir)? {
81 let entry = entry?;
82 let path = entry.path();
83
84 if path.is_dir() {
85 discover_rust_files_recursive(&path, files)?;
86 } else if path.extension() == Some(std::ffi::OsStr::new("rs")) {
87 files.push(path.canonicalize()?);
88 }
89 }
90 Ok(())
91}
92
93pub fn parse_file(path: &Path) -> std::result::Result<ParsedFile, ParseError> {
98 let original_source = fs::read_to_string(path)?;
99
100 let token_stream = TokenStream::from_str(&original_source)
101 .map_err(|e| ParseError::ParseFailed(format!("Failed to tokenize: {}", e)))?;
102
103 let content = token_stream
104 .into_token_iter()
105 .parse::<ModuleContent>()
106 .map_err(|e| ParseError::ParseFailed(format!("Failed to parse module: {}", e)))?;
107
108 Ok(ParsedFile {
109 path: path.to_path_buf(),
110 content,
111 original_source,
112 })
113}
114
115pub fn get_or_create_docs_path(
120 source_file: &Path,
121 dry_run: bool,
122) -> std::result::Result<String, ConfigError> {
123 match syncdoc_core::config::get_docs_path(source_file.to_str().unwrap()) {
125 Ok(path) => Ok(path),
126 Err(_) => {
127 if !dry_run {
129 let source_dir = source_file.parent().ok_or_else(|| {
130 ConfigError::Other("Source file has no parent directory".to_string())
131 })?;
132
133 let manifest_dir = syncdoc_core::path_utils::find_manifest_dir(source_dir)
134 .ok_or_else(|| ConfigError::Other("Could not find Cargo.toml".to_string()))?;
135
136 let cargo_toml_path = manifest_dir.join("Cargo.toml");
137
138 let mut content = fs::read_to_string(&cargo_toml_path)?;
140
141 if !content.contains("[package.metadata.syncdoc]") {
143 content.push_str("\n[package.metadata.syncdoc]\n");
145 content.push_str("docs-path = \"docs\"\n");
146
147 fs::write(&cargo_toml_path, content)?;
149 }
150 }
151
152 Ok("docs".to_string())
153 }
154 }
155}
156
157#[cfg(test)]
158mod tests;