1use anyhow::Result;
6use std::sync::Arc;
7
8use super::components::*;
9
10#[derive(Debug, Clone)]
16pub struct InstallResult {
17 pub installed_dependencies: Vec<ResolvedDependency>,
18 pub updated_config: bool,
19 pub updated_lock_file: bool,
20 pub cache_updates: usize,
21 pub warnings: Vec<String>,
22}
23
24impl InstallResult {
25 pub fn success() -> Self {
26 Self {
27 installed_dependencies: Vec::new(),
28 updated_config: false,
29 updated_lock_file: false,
30 cache_updates: 0,
31 warnings: Vec::new(),
32 }
33 }
34
35 pub fn summary(&self) -> String {
36 format!(
37 "Installed {} dependencies, updated {} cache entries",
38 self.installed_dependencies.len(),
39 self.cache_updates
40 )
41 }
42}
43
44#[derive(Debug, Clone)]
46pub struct InstallPlan {
47 pub dependencies_to_install: Vec<DependencySpec>,
48 pub resolved_dependencies: Vec<ResolvedDependency>,
49 pub estimated_cache_size: u64,
50 pub required_permissions: Vec<String>,
51}
52
53#[derive(Debug, Clone)]
55pub struct GenerationOptions {
56 pub input_path: std::path::PathBuf,
57 pub output_path: std::path::PathBuf,
58 pub clean_before_generate: bool,
59 pub generate_scaffold: bool,
60 pub format_code: bool,
61 pub run_checks: bool,
62}
63
64#[derive(Clone)]
70pub struct ValidationPipeline {
71 config_manager: Arc<dyn ConfigManager>,
72 dependency_resolver: Arc<dyn DependencyResolver>,
73 service_discovery: Arc<dyn ServiceDiscovery>,
74 network_validator: Arc<dyn NetworkValidator>,
75 fingerprint_validator: Arc<dyn FingerprintValidator>,
76}
77
78impl ValidationPipeline {
79 pub fn new(
80 config_manager: Arc<dyn ConfigManager>,
81 dependency_resolver: Arc<dyn DependencyResolver>,
82 service_discovery: Arc<dyn ServiceDiscovery>,
83 network_validator: Arc<dyn NetworkValidator>,
84 fingerprint_validator: Arc<dyn FingerprintValidator>,
85 ) -> Self {
86 Self {
87 config_manager,
88 dependency_resolver,
89 service_discovery,
90 network_validator,
91 fingerprint_validator,
92 }
93 }
94
95 pub async fn validate_project(&self) -> Result<ValidationReport> {
97 let config_validation = self.config_manager.validate_config().await?;
99
100 if !config_validation.is_valid {
102 return Ok(ValidationReport {
103 is_valid: false,
104 config_validation,
105 dependency_validation: vec![],
106 network_validation: vec![],
107 fingerprint_validation: vec![],
108 conflicts: vec![],
109 });
110 }
111
112 let config = self
114 .config_manager
115 .load_config(
116 self.config_manager
117 .get_project_root()
118 .join("Actr.toml")
119 .as_path(),
120 )
121 .await?;
122 let dependency_specs = self.extract_dependency_specs(&config)?;
123 let resolved_dependencies = self
124 .dependency_resolver
125 .resolve_dependencies(&dependency_specs)
126 .await?;
127
128 let conflicts = self
130 .dependency_resolver
131 .check_conflicts(&resolved_dependencies)
132 .await?;
133
134 let dependency_validation = self.validate_dependencies(&dependency_specs).await?;
136 let network_validation = self
137 .validate_network_connectivity(&resolved_dependencies)
138 .await?;
139 let fingerprint_validation = self.validate_fingerprints(&resolved_dependencies).await?;
140
141 let is_valid = config_validation.is_valid
142 && dependency_validation.iter().all(|d| d.is_available)
143 && network_validation.iter().all(|n| n.is_reachable)
144 && fingerprint_validation.iter().all(|f| f.is_valid)
145 && conflicts.is_empty();
146
147 Ok(ValidationReport {
148 is_valid,
149 config_validation,
150 dependency_validation,
151 network_validation,
152 fingerprint_validation,
153 conflicts,
154 })
155 }
156
157 pub async fn validate_dependencies(
159 &self,
160 specs: &[DependencySpec],
161 ) -> Result<Vec<DependencyValidation>> {
162 let mut results = Vec::new();
163
164 for spec in specs {
165 let validation = match self
166 .service_discovery
167 .check_service_availability(&spec.uri)
168 .await
169 {
170 Ok(status) => DependencyValidation {
171 dependency: spec.name.clone(),
172 is_available: status.is_available,
173 resolved_uri: Some(spec.uri.clone()),
174 error: None,
175 },
176 Err(e) => DependencyValidation {
177 dependency: spec.name.clone(),
178 is_available: false,
179 resolved_uri: None,
180 error: Some(e.to_string()),
181 },
182 };
183 results.push(validation);
184 }
185
186 Ok(results)
187 }
188
189 async fn validate_network_connectivity(
191 &self,
192 deps: &[ResolvedDependency],
193 ) -> Result<Vec<NetworkValidation>> {
194 let uris: Vec<String> = deps.iter().map(|d| d.uri.clone()).collect();
195 let network_results = self.network_validator.batch_check(&uris).await?;
196
197 Ok(network_results
198 .into_iter()
199 .map(|result| NetworkValidation {
200 uri: result.uri,
201 is_reachable: result.connectivity.is_reachable,
202 latency_ms: result.connectivity.response_time_ms,
203 error: result.connectivity.error,
204 })
205 .collect())
206 }
207
208 async fn validate_fingerprints(
210 &self,
211 deps: &[ResolvedDependency],
212 ) -> Result<Vec<FingerprintValidation>> {
213 let mut results = Vec::new();
214
215 for dep in deps {
216 let expected = Fingerprint {
217 algorithm: "sha256".to_string(),
218 value: dep.fingerprint.clone(),
219 };
220
221 let service_info = match self.service_discovery.get_service_details(&dep.uri).await {
223 Ok(details) => details.info,
224 Err(e) => {
225 results.push(FingerprintValidation {
226 dependency: dep.spec.name.clone(),
227 expected,
228 actual: None,
229 is_valid: false,
230 error: Some(e.to_string()),
231 });
232 continue;
233 }
234 };
235
236 match self
237 .fingerprint_validator
238 .compute_service_fingerprint(&service_info)
239 .await
240 {
241 Ok(actual) => {
242 let is_valid = self
243 .fingerprint_validator
244 .verify_fingerprint(&expected, &actual)
245 .await
246 .unwrap_or(false);
247 results.push(FingerprintValidation {
248 dependency: dep.spec.name.clone(),
249 expected,
250 actual: Some(actual),
251 is_valid,
252 error: None,
253 });
254 }
255 Err(e) => {
256 results.push(FingerprintValidation {
257 dependency: dep.spec.name.clone(),
258 expected,
259 actual: None,
260 is_valid: false,
261 error: Some(e.to_string()),
262 });
263 }
264 }
265 }
266
267 Ok(results)
268 }
269
270 fn extract_dependency_specs(&self, config: &Config) -> Result<Vec<DependencySpec>> {
272 let mut specs = Vec::new();
273
274 for dependency in &config.dependencies {
275 let uri = format!(
276 "actr://{}:{}+{}@v1/",
277 dependency.realm.realm_id,
278 dependency.actr_type.manufacturer,
279 dependency.actr_type.name
280 );
281 specs.push(DependencySpec {
282 name: dependency.alias.clone(),
283 uri,
284 version: None,
285 fingerprint: dependency.fingerprint.clone(),
286 });
287 }
288
289 Ok(specs)
290 }
291}
292
293pub struct InstallPipeline {
299 validation_pipeline: ValidationPipeline,
300 config_manager: Arc<dyn ConfigManager>,
301 cache_manager: Arc<dyn CacheManager>,
302 #[allow(dead_code)]
303 proto_processor: Arc<dyn ProtoProcessor>,
304}
305
306impl InstallPipeline {
307 pub fn new(
308 validation_pipeline: ValidationPipeline,
309 config_manager: Arc<dyn ConfigManager>,
310 cache_manager: Arc<dyn CacheManager>,
311 proto_processor: Arc<dyn ProtoProcessor>,
312 ) -> Self {
313 Self {
314 validation_pipeline,
315 config_manager,
316 cache_manager,
317 proto_processor,
318 }
319 }
320
321 pub async fn install_dependencies(&self, specs: &[DependencySpec]) -> Result<InstallResult> {
323 let validation_report = self
325 .validation_pipeline
326 .validate_dependencies(specs)
327 .await?;
328
329 let failed_validations: Vec<_> = validation_report
331 .iter()
332 .filter(|v| !v.is_available)
333 .collect();
334
335 if !failed_validations.is_empty() {
336 return Err(anyhow::anyhow!(
337 "依赖验证失败: {}",
338 failed_validations
339 .iter()
340 .map(|v| format!(
341 "{}: {}",
342 v.dependency,
343 v.error.as_deref().unwrap_or("unknown error")
344 ))
345 .collect::<Vec<_>>()
346 .join(", ")
347 ));
348 }
349
350 let backup = self.config_manager.backup_config().await?;
352
353 match self.execute_atomic_install(specs).await {
354 Ok(result) => {
355 self.config_manager.remove_backup(backup).await?;
357 Ok(result)
358 }
359 Err(e) => {
360 self.config_manager.restore_backup(backup).await?;
362 Err(e)
363 }
364 }
365 }
366
367 async fn execute_atomic_install(&self, specs: &[DependencySpec]) -> Result<InstallResult> {
369 let mut result = InstallResult::success();
370
371 for spec in specs {
372 self.config_manager.update_dependency(spec).await?;
374 result.updated_config = true;
375
376 let service_details = self
378 .validation_pipeline
379 .service_discovery
380 .get_service_details(&spec.uri)
381 .await?;
382
383 self.cache_manager
384 .cache_proto(&spec.uri, &service_details.proto_files)
385 .await?;
386 result.cache_updates += 1;
387
388 let resolved_dep = ResolvedDependency {
390 spec: spec.clone(),
391 uri: spec.uri.clone(),
392 resolved_version: service_details.info.version,
393 fingerprint: service_details.info.fingerprint,
394 proto_files: service_details.proto_files,
395 };
396 result.installed_dependencies.push(resolved_dep);
397 }
398
399 self.update_lock_file(&result.installed_dependencies)
401 .await?;
402 result.updated_lock_file = true;
403
404 Ok(result)
405 }
406
407 async fn update_lock_file(&self, dependencies: &[ResolvedDependency]) -> Result<()> {
409 println!("Updating lock file: {} dependencies", dependencies.len());
412 Ok(())
413 }
414}
415
416pub struct GenerationPipeline {
422 #[allow(dead_code)]
423 config_manager: Arc<dyn ConfigManager>,
424 proto_processor: Arc<dyn ProtoProcessor>,
425 #[allow(dead_code)]
426 cache_manager: Arc<dyn CacheManager>,
427}
428
429impl GenerationPipeline {
430 pub fn new(
431 config_manager: Arc<dyn ConfigManager>,
432 proto_processor: Arc<dyn ProtoProcessor>,
433 cache_manager: Arc<dyn CacheManager>,
434 ) -> Self {
435 Self {
436 config_manager,
437 proto_processor,
438 cache_manager,
439 }
440 }
441
442 pub async fn generate_code(&self, options: &GenerationOptions) -> Result<GenerationResult> {
444 if options.clean_before_generate {
446 self.clean_output_directory(&options.output_path).await?;
447 }
448
449 let local_protos = self
451 .proto_processor
452 .discover_proto_files(&options.input_path)
453 .await?;
454
455 let dependency_protos = self.load_dependency_protos().await?;
457
458 let all_protos = [local_protos, dependency_protos].concat();
460 let validation = self
461 .proto_processor
462 .validate_proto_syntax(&all_protos)
463 .await?;
464
465 if !validation.is_valid {
466 return Err(anyhow::anyhow!("Proto file syntax validation failed"));
467 }
468
469 let mut generation_result = self
471 .proto_processor
472 .generate_code(&options.input_path, &options.output_path)
473 .await?;
474
475 if options.format_code {
477 self.format_generated_code(&generation_result.generated_files)
478 .await?;
479 }
480
481 if options.run_checks {
482 let check_result = self
483 .run_code_checks(&generation_result.generated_files)
484 .await?;
485 generation_result.warnings.extend(check_result.warnings);
486 generation_result.errors.extend(check_result.errors);
487 }
488
489 Ok(generation_result)
490 }
491
492 async fn clean_output_directory(&self, output_path: &std::path::Path) -> Result<()> {
494 if output_path.exists() {
495 std::fs::remove_dir_all(output_path)?;
496 }
497 std::fs::create_dir_all(output_path)?;
498 Ok(())
499 }
500
501 async fn load_dependency_protos(&self) -> Result<Vec<ProtoFile>> {
503 Ok(Vec::new())
505 }
506
507 async fn format_generated_code(&self, files: &[std::path::PathBuf]) -> Result<()> {
509 for file in files {
510 if file.extension().and_then(|s| s.to_str()) == Some("rs") {
511 let output = std::process::Command::new("rustfmt").arg(file).output()?;
513
514 if !output.status.success() {
515 eprintln!(
516 "rustfmt warning: {}",
517 String::from_utf8_lossy(&output.stderr)
518 );
519 }
520 }
521 }
522 Ok(())
523 }
524
525 async fn run_code_checks(&self, files: &[std::path::PathBuf]) -> Result<GenerationResult> {
527 Ok(GenerationResult {
529 generated_files: files.to_vec(),
530 warnings: vec![],
531 errors: vec![],
532 })
533 }
534}