codegraph_swift/
parser_impl.rs

1//! Swift parser implementation
2
3use 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
13/// Swift language parser
14pub struct SwiftParser {
15    config: ParserConfig,
16    metrics: Mutex<ParserMetrics>,
17}
18
19impl SwiftParser {
20    /// Create a new Swift parser with default configuration
21    pub fn new() -> Self {
22        Self {
23            config: ParserConfig::default(),
24            metrics: Mutex::new(ParserMetrics::default()),
25        }
26    }
27
28    /// Create a new Swift parser with custom configuration
29    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        // Extract code entities from source
113        let ir = extractor::extract(source, file_path, &self.config)?;
114
115        // Map IR to graph nodes and edges
116        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}