es_fluent_sc_parser/
lib.rs1#![doc = include_str!("../README.md")]
2
3use crate::visitor::FtlVisitor;
4use std::fs;
5use std::path::{Path, PathBuf};
6use walkdir::WalkDir;
7
8pub mod error;
9mod processor;
10mod visitor;
11
12use error::FluentScParserError;
13use es_fluent_core::registry::FtlTypeInfo;
14
15pub fn parse_directory(dir_path: &Path) -> Result<Vec<FtlTypeInfo>, FluentScParserError> {
27 log::info!(
28 "Starting FTL type info parsing in directory: {}",
29 dir_path.display()
30 );
31
32 let rust_files: Vec<PathBuf> = WalkDir::new(dir_path)
33 .into_iter()
34 .filter_map(|entry_result| match entry_result {
35 Ok(entry) => {
36 let path = entry.path();
37 if path.is_file()
38 && let Some(ext) = path.extension()
39 && ext == "rs"
40 {
41 Some(Ok(path.to_path_buf()))
42 } else {
43 None
44 }
45 },
46 Err(e) => Some(Err(FluentScParserError::WalkDir(dir_path.to_path_buf(), e))),
47 })
48 .collect::<Result<Vec<_>, _>>()?;
49
50 log::debug!("Found {} Rust files to parse.", rust_files.len());
51
52 let file_results: Result<Vec<Vec<FtlTypeInfo>>, FluentScParserError> = rust_files
53 .iter()
54 .map(|file_path| {
55 log::trace!("Parsing file: {}", file_path.display());
56 let content = fs::read_to_string(file_path)
57 .map_err(|e| FluentScParserError::Io(file_path.clone(), e))?;
58 let syntax_tree = syn::parse_file(&content)
59 .map_err(|e| FluentScParserError::Syn(file_path.clone(), e))?;
60
61 let mut visitor = FtlVisitor::new(file_path);
62 syn::visit::visit_file(&mut visitor, &syntax_tree);
63 Ok(visitor.type_infos().to_owned())
64 })
65 .collect();
66
67 let results: Vec<FtlTypeInfo> = file_results?
68 .into_iter()
69 .filter(|type_infos| !type_infos.is_empty())
70 .flatten()
71 .collect::<std::collections::HashSet<_>>()
72 .into_iter()
73 .collect();
74
75 log::info!(
76 "Finished parsing. Found {} FTL type info entries.",
77 results.len()
78 );
79 Ok(results)
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85 use std::fs;
86 use tempfile::TempDir;
87
88 #[test]
89 fn test_parse_directory_empty() {
90 let temp_dir = TempDir::new().unwrap();
91 let result = parse_directory(temp_dir.path());
92 assert!(result.is_ok());
93 assert_eq!(result.unwrap().len(), 0);
94 }
95
96 #[test]
97 fn test_parse_directory_with_nonexistent_path() {
98 let non_existent_path = Path::new("/non/existent/path");
99 let result = parse_directory(non_existent_path);
100 assert!(result.is_err());
101 }
102
103 #[test]
104 fn test_parse_directory_with_rust_file() {
105 let temp_dir = TempDir::new().unwrap();
106 let rust_file_path = temp_dir.path().join("test.rs");
107
108 let rust_content = r#"
109use es_fluent_core::EsFluent;
110
111#[derive(EsFluent)]
112#[fluent(display = "fluent")]
113pub enum TestEnum {
114 Variant1,
115 Variant2,
116}
117"#;
118
119 fs::write(&rust_file_path, rust_content).unwrap();
120
121 let result = parse_directory(temp_dir.path());
122 assert!(result.is_ok());
123
124 let type_infos = result.unwrap();
125 assert!(!type_infos.is_empty());
126 }
127
128 #[test]
129 fn test_parse_directory_with_multiple_rust_files() {
130 let temp_dir = TempDir::new().unwrap();
131
132 let rust_file1_path = temp_dir.path().join("test1.rs");
133 let rust_content1 = r#"
134use es_fluent_core::EsFluent;
135
136#[derive(EsFluent)]
137#[fluent(display = "fluent")]
138pub enum TestEnum1 {
139 VariantA,
140}
141"#;
142
143 fs::write(&rust_file1_path, rust_content1).unwrap();
144
145 let rust_file2_path = temp_dir.path().join("test2.rs");
146 let rust_content2 = r#"
147use es_fluent_core::EsFluent;
148
149#[derive(EsFluent)]
150#[fluent(display = "fluent")]
151pub enum TestEnum2 {
152 VariantB,
153}
154"#;
155 fs::write(&rust_file2_path, rust_content2).unwrap();
156
157 let result = parse_directory(temp_dir.path());
158 assert!(result.is_ok());
159
160 let type_infos = result.unwrap();
161 assert!(type_infos.len() >= 2);
162 }
163
164 #[test]
165 fn test_parse_directory_with_non_rust_file() {
166 let temp_dir = TempDir::new().unwrap();
167 let non_rust_file_path = temp_dir.path().join("test.txt");
168 fs::write(&non_rust_file_path, "not a rust file").unwrap();
169
170 let result = parse_directory(temp_dir.path());
171 assert!(result.is_ok());
172 assert_eq!(result.unwrap().len(), 0);
173 }
174}