codegraph_swift/
parser_impl.rs1use codegraph::CodeGraph;
4use codegraph_parser_api::{CodeParser, FileInfo, ParserConfig, ParserError, ParserMetrics};
5use std::fs;
6use std::path::Path;
7use std::sync::Mutex;
8use std::time::{Duration, Instant};
9
10use crate::extractor;
11use crate::mapper;
12
13pub struct SwiftParser {
15 config: ParserConfig,
16 metrics: Mutex<ParserMetrics>,
17}
18
19impl SwiftParser {
20 pub fn new() -> Self {
22 Self {
23 config: ParserConfig::default(),
24 metrics: Mutex::new(ParserMetrics::default()),
25 }
26 }
27
28 pub fn with_config(config: ParserConfig) -> Self {
30 Self {
31 config,
32 metrics: Mutex::new(ParserMetrics::default()),
33 }
34 }
35
36 fn update_metrics(
37 &self,
38 success: bool,
39 duration: Duration,
40 entities: usize,
41 relationships: usize,
42 ) {
43 let mut metrics = self.metrics.lock().unwrap();
44 metrics.files_attempted += 1;
45 if success {
46 metrics.files_succeeded += 1;
47 } else {
48 metrics.files_failed += 1;
49 }
50 metrics.total_parse_time += duration;
51 metrics.total_entities += entities;
52 metrics.total_relationships += relationships;
53 }
54}
55
56impl Default for SwiftParser {
57 fn default() -> Self {
58 Self::new()
59 }
60}
61
62impl CodeParser for SwiftParser {
63 fn language(&self) -> &str {
64 "swift"
65 }
66
67 fn file_extensions(&self) -> &[&str] {
68 &[".swift"]
69 }
70
71 fn can_parse(&self, path: &Path) -> bool {
72 path.extension()
73 .and_then(|ext| ext.to_str())
74 .map(|ext| ext == "swift")
75 .unwrap_or(false)
76 }
77
78 fn parse_file(&self, path: &Path, graph: &mut CodeGraph) -> Result<FileInfo, ParserError> {
79 let start = Instant::now();
80 let metadata =
81 fs::metadata(path).map_err(|e| ParserError::IoError(path.to_path_buf(), e))?;
82
83 if metadata.len() as usize > self.config.max_file_size {
84 return Err(ParserError::FileTooLarge(
85 path.to_path_buf(),
86 metadata.len() as usize,
87 ));
88 }
89
90 let source =
91 fs::read_to_string(path).map_err(|e| ParserError::IoError(path.to_path_buf(), e))?;
92 let result = self.parse_source(&source, path, graph);
93
94 let duration = start.elapsed();
95 if let Ok(ref info) = result {
96 self.update_metrics(true, duration, info.entity_count(), 0);
97 } else {
98 self.update_metrics(false, duration, 0, 0);
99 }
100
101 result
102 }
103
104 fn parse_source(
105 &self,
106 source: &str,
107 file_path: &Path,
108 graph: &mut CodeGraph,
109 ) -> Result<FileInfo, ParserError> {
110 let start_time = std::time::Instant::now();
111
112 let ir = extractor::extract(source, file_path, &self.config)?;
114
115 let mut file_info = mapper::ir_to_graph(&ir, graph, file_path)?;
117
118 file_info.parse_time = start_time.elapsed();
119 file_info.byte_count = source.len();
120
121 Ok(file_info)
122 }
123
124 fn config(&self) -> &ParserConfig {
125 &self.config
126 }
127
128 fn metrics(&self) -> ParserMetrics {
129 self.metrics.lock().unwrap().clone()
130 }
131
132 fn reset_metrics(&mut self) {
133 *self.metrics.lock().unwrap() = ParserMetrics::default();
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140
141 #[test]
142 fn test_language() {
143 let parser = SwiftParser::new();
144 assert_eq!(parser.language(), "swift");
145 }
146
147 #[test]
148 fn test_file_extensions() {
149 let parser = SwiftParser::new();
150 let extensions = parser.file_extensions();
151 assert!(extensions.contains(&".swift"));
152 }
153
154 #[test]
155 fn test_can_parse() {
156 let parser = SwiftParser::new();
157 assert!(parser.can_parse(Path::new("main.swift")));
158 assert!(parser.can_parse(Path::new("ViewController.swift")));
159 assert!(!parser.can_parse(Path::new("main.rs")));
160 assert!(!parser.can_parse(Path::new("main.cpp")));
161 }
162}