ricecoder_research/dependency_analyzer/
dart_parser.rs1use crate::error::ResearchError;
4use crate::models::Dependency;
5use std::path::Path;
6use tracing::debug;
7
8#[derive(Debug)]
10pub struct DartParser;
11
12impl DartParser {
13 pub fn new() -> Self {
15 DartParser
16 }
17
18 pub fn parse(&self, root: &Path) -> Result<Vec<Dependency>, ResearchError> {
20 let pubspec_path = root.join("pubspec.yaml");
21
22 if !pubspec_path.exists() {
23 return Ok(Vec::new());
24 }
25
26 debug!("Parsing Dart/Flutter dependencies from {:?}", pubspec_path);
27
28 let content = std::fs::read_to_string(&pubspec_path).map_err(|e| {
29 ResearchError::DependencyParsingFailed {
30 language: "Dart".to_string(),
31 path: Some(pubspec_path.clone()),
32 reason: format!("Failed to read pubspec.yaml: {}", e),
33 }
34 })?;
35
36 let pubspec: serde_yaml::Value =
37 serde_yaml::from_str(&content).map_err(|e| ResearchError::DependencyParsingFailed {
38 language: "Dart".to_string(),
39 path: Some(pubspec_path.clone()),
40 reason: format!("Failed to parse pubspec.yaml: {}", e),
41 })?;
42
43 let mut dependencies = Vec::new();
44
45 if let Some(deps) = pubspec.get("dependencies").and_then(|d| d.as_mapping()) {
47 for (key, value) in deps {
48 if let Some(name) = key.as_str() {
49 if name != "flutter" {
50 let version = if let Some(version_str) = value.as_str() {
51 version_str.to_string()
52 } else if let Some(mapping) = value.as_mapping() {
53 if let Some(version) =
54 mapping.get(serde_yaml::Value::String("version".to_string()))
55 {
56 version.as_str().unwrap_or("*").to_string()
57 } else {
58 "*".to_string()
59 }
60 } else {
61 "*".to_string()
62 };
63
64 dependencies.push(Dependency {
65 name: name.to_string(),
66 version: version.clone(),
67 constraints: Some(version),
68 is_dev: false,
69 });
70 }
71 }
72 }
73 }
74
75 if let Some(deps) = pubspec.get("dev_dependencies").and_then(|d| d.as_mapping()) {
77 for (key, value) in deps {
78 if let Some(name) = key.as_str() {
79 let version = if let Some(version_str) = value.as_str() {
80 version_str.to_string()
81 } else if let Some(mapping) = value.as_mapping() {
82 if let Some(version) =
83 mapping.get(serde_yaml::Value::String("version".to_string()))
84 {
85 version.as_str().unwrap_or("*").to_string()
86 } else {
87 "*".to_string()
88 }
89 } else {
90 "*".to_string()
91 };
92
93 dependencies.push(Dependency {
94 name: name.to_string(),
95 version: version.clone(),
96 constraints: Some(version),
97 is_dev: true,
98 });
99 }
100 }
101 }
102
103 Ok(dependencies)
104 }
105
106 pub fn has_manifest(&self, root: &Path) -> bool {
108 root.join("pubspec.yaml").exists()
109 }
110}
111
112impl Default for DartParser {
113 fn default() -> Self {
114 Self::new()
115 }
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121 use std::fs;
122 use tempfile::TempDir;
123
124 #[test]
125 fn test_dart_parser_creation() {
126 let _parser = DartParser::new();
127 assert!(true);
128 }
129
130 #[test]
131 fn test_dart_parser_no_manifest() {
132 let _parser = DartParser::new();
133 let temp_dir = TempDir::new().unwrap();
134 let result = _parser.parse(temp_dir.path()).unwrap();
135 assert!(result.is_empty());
136 }
137
138 #[test]
139 fn test_dart_parser_simple_dependencies() {
140 let parser = DartParser::new();
141 let temp_dir = TempDir::new().unwrap();
142
143 let pubspec = r#"
144name: my_app
145version: 1.0.0
146
147dependencies:
148 flutter:
149 sdk: flutter
150 provider: ^6.0.0
151 http: ^0.13.0
152
153dev_dependencies:
154 flutter_test:
155 sdk: flutter
156 test: ^1.20.0
157"#;
158
159 fs::write(temp_dir.path().join("pubspec.yaml"), pubspec).unwrap();
160
161 let deps = parser.parse(temp_dir.path()).unwrap();
162 assert!(deps.len() >= 2);
163
164 let provider = deps.iter().find(|d| d.name == "provider").unwrap();
165 assert_eq!(provider.version, "^6.0.0");
166 assert!(!provider.is_dev);
167 }
168
169 #[test]
170 fn test_dart_parser_has_manifest() {
171 let parser = DartParser::new();
172 let temp_dir = TempDir::new().unwrap();
173
174 assert!(!parser.has_manifest(temp_dir.path()));
175
176 fs::write(temp_dir.path().join("pubspec.yaml"), "").unwrap();
177 assert!(parser.has_manifest(temp_dir.path()));
178 }
179}