1use anyhow::Result;
6use async_trait::async_trait;
7
8use crate::core::{
9 ActrCliError, Command, CommandContext, CommandResult, ComponentType, DependencySpec,
10 ServiceInfo,
11};
12
13pub struct DiscoveryCommand {
15 filter: Option<String>,
16 #[allow(dead_code)]
17 verbose: bool,
18 auto_install: bool,
19}
20
21#[async_trait]
22impl Command for DiscoveryCommand {
23 async fn execute(&self, context: &CommandContext) -> Result<CommandResult> {
24 let (service_discovery, user_interface, _config_manager) = {
26 let container = context.container.lock().unwrap();
27 (
28 container.get_service_discovery()?,
29 container.get_user_interface()?,
30 container.get_config_manager()?,
31 )
32 };
33
34 println!("🔍 正在扫描网络中的 Actor 服务...");
36
37 let filter = self.create_service_filter();
38 let services = service_discovery.discover_services(filter.as_ref()).await?;
39
40 if services.is_empty() {
41 println!("ℹ️ 当前网络中没有发现可用的 Actor 服务");
42 return Ok(CommandResult::Success("No services discovered".to_string()));
43 }
44
45 self.display_services_table(&services);
47
48 let selected_index = user_interface
50 .select_service_from_list(&services, |s| format!("{} ({})", s.name, s.version))
51 .await?;
52
53 let selected_service = &services[selected_index];
54
55 self.display_service_details(selected_service).await?;
57
58 let action_menu = vec![
60 "查看服务详情".to_string(),
61 "导出 proto 文件".to_string(),
62 "添加到配置文件".to_string(),
63 ];
64
65 let action_choice = user_interface
66 .select_string_from_list(&action_menu, |s| s.clone())
67 .await?;
68
69 match action_choice {
70 0 => {
71 self.show_detailed_service_info(selected_service, &service_discovery)
73 .await?;
74 Ok(CommandResult::Success(
75 "Service details displayed".to_string(),
76 ))
77 }
78 1 => {
79 self.export_proto_files(selected_service, &service_discovery)
81 .await?;
82 Ok(CommandResult::Success("Proto files exported".to_string()))
83 }
84 2 => {
85 self.add_to_config_with_validation(selected_service, context)
87 .await
88 }
89 _ => Ok(CommandResult::Success("Invalid choice".to_string())),
90 }
91 }
92
93 fn required_components(&self) -> Vec<ComponentType> {
94 vec![
96 ComponentType::ServiceDiscovery, ComponentType::UserInterface, ComponentType::ConfigManager, ComponentType::DependencyResolver, ComponentType::NetworkValidator, ComponentType::FingerprintValidator, ComponentType::CacheManager, ComponentType::ProtoProcessor, ]
105 }
106
107 fn name(&self) -> &str {
108 "discovery"
109 }
110
111 fn description(&self) -> &str {
112 "发现网络中可用的 Actor 服务 (复用架构 + check-first)"
113 }
114}
115
116impl DiscoveryCommand {
117 pub fn new(filter: Option<String>, verbose: bool, auto_install: bool) -> Self {
118 Self {
119 filter,
120 verbose,
121 auto_install,
122 }
123 }
124
125 fn create_service_filter(&self) -> Option<crate::core::ServiceFilter> {
127 self.filter
128 .as_ref()
129 .map(|pattern| crate::core::ServiceFilter {
130 name_pattern: Some(pattern.clone()),
131 version_range: None,
132 tags: None,
133 })
134 }
135
136 fn display_services_table(&self, services: &[ServiceInfo]) {
138 println!();
139 println!("🔍 发现的 Actor 服务:");
140 println!();
141 println!("┌─────────────────┬─────────┬─────────────────────────────────┐");
142 println!("│ 服务名称 │ 版本 │ 简介 │");
143 println!("├─────────────────┼─────────┼─────────────────────────────────┤");
144
145 for service in services {
146 let description = service
147 .description
148 .as_deref()
149 .unwrap_or("无描述")
150 .chars()
151 .take(28)
152 .collect::<String>();
153
154 println!(
155 "│ {:15} │ {:7} │ {:31} │",
156 service.name.chars().take(15).collect::<String>(),
157 service.version.chars().take(7).collect::<String>(),
158 description
159 );
160 }
161
162 println!("└─────────────────┴─────────┴─────────────────────────────────┘");
163 println!();
164 println!("→ 使用 ↑↓ 选择服务,回车查看选项,q 退出");
165 println!();
166 }
167
168 async fn display_service_details(&self, service: &ServiceInfo) -> Result<()> {
170 println!("📋 选择的服务: {} ({})", service.name, service.version);
171 if let Some(desc) = &service.description {
172 println!("📝 描述: {desc}");
173 }
174 println!("🔗 URI: {}", service.uri);
175 println!("🔐 指纹: {}", service.fingerprint);
176 println!("📊 方法数量: {}", service.methods.len());
177 println!();
178 Ok(())
179 }
180
181 async fn show_detailed_service_info(
183 &self,
184 service: &ServiceInfo,
185 service_discovery: &std::sync::Arc<dyn crate::core::ServiceDiscovery>,
186 ) -> Result<()> {
187 println!("📖 {} 详细信息:", service.name);
188 println!("════════════════════════════════════════");
189
190 let details = service_discovery.get_service_details(&service.uri).await?;
191
192 println!("🏷️ 服务名称: {}", details.info.name);
193 println!("📦 版本: {}", details.info.version);
194 println!("🔗 URI: {}", details.info.uri);
195 println!("🔐 指纹: {}", details.info.fingerprint);
196
197 if let Some(desc) = &details.info.description {
198 println!("📝 描述: {desc}");
199 }
200
201 println!();
202 println!("📋 可用方法:");
203 for method in &details.info.methods {
204 println!(
205 " • {}: {} → {}",
206 method.name, method.input_type, method.output_type
207 );
208 }
209
210 if !details.dependencies.is_empty() {
211 println!();
212 println!("🔗 依赖服务:");
213 for dep in &details.dependencies {
214 println!(" • {dep}");
215 }
216 }
217
218 println!();
219 println!("📁 Proto 文件:");
220 for proto in &details.proto_files {
221 println!(" • {} ({} 个服务)", proto.name, proto.services.len());
222 }
223
224 Ok(())
225 }
226
227 async fn export_proto_files(
229 &self,
230 service: &ServiceInfo,
231 service_discovery: &std::sync::Arc<dyn crate::core::ServiceDiscovery>,
232 ) -> Result<()> {
233 println!("📤 正在导出 {} 的 proto 文件...", service.name);
234
235 let proto_files = service_discovery.get_service_proto(&service.uri).await?;
236
237 for proto in &proto_files {
238 let file_path = format!("./exported_{}", proto.name);
239 std::fs::write(&file_path, &proto.content)?;
240 println!("✅ 已导出: {file_path}");
241 }
242
243 println!("🎉 导出完成,共 {} 个文件", proto_files.len());
244 Ok(())
245 }
246
247 async fn add_to_config_with_validation(
249 &self,
250 service: &ServiceInfo,
251 context: &CommandContext,
252 ) -> Result<CommandResult> {
253 let (config_manager, user_interface) = {
254 let container = context.container.lock().unwrap();
255 (
256 container.get_config_manager()?,
257 container.get_user_interface()?,
258 )
259 };
260
261 let dependency_spec = DependencySpec {
263 name: service.name.clone(),
264 uri: service.uri.clone(),
265 version: Some(service.version.clone()),
266 fingerprint: Some(service.fingerprint.clone()),
267 };
268
269 println!("📝 正在添加 {} 到配置文件...", service.name);
270
271 let backup = config_manager.backup_config().await?;
273
274 match config_manager.update_dependency(&dependency_spec).await {
276 Ok(_) => {
277 println!("✅ 已添加 {} 到配置文件", service.name);
278 }
279 Err(e) => {
280 config_manager.restore_backup(backup).await?;
281 return Err(ActrCliError::Config {
282 message: format!("配置更新失败: {e}"),
283 }
284 .into());
285 }
286 }
287
288 println!();
290 println!("🔍 正在验证新依赖...");
291
292 let validation_pipeline = {
293 let mut container = context.container.lock().unwrap();
294 container.get_validation_pipeline()?
295 };
296
297 match validation_pipeline
298 .validate_dependencies(&[dependency_spec.clone()])
299 .await
300 {
301 Ok(validation_results) => {
302 let all_passed = validation_results.iter().all(|v| v.is_available);
303
304 if !all_passed {
305 println!("❌ 依赖验证失败,正在回滚配置修改...");
307 config_manager.restore_backup(backup).await?;
308
309 for validation in &validation_results {
311 if !validation.is_available {
312 println!(
313 " • {}: {}",
314 validation.dependency,
315 validation.error.as_deref().unwrap_or("验证失败")
316 );
317 }
318 }
319
320 return Err(ActrCliError::ValidationFailed {
321 details: "依赖验证失败".to_string(),
322 }
323 .into());
324 } else {
325 println!(" ├─ 📋 服务存在性检查 ✅");
327 println!(" ├─ 🌐 网络连通性测试 ✅");
328 println!(" └─ 🔐 指纹完整性验证 ✅");
329
330 config_manager.remove_backup(backup).await?;
332 }
333 }
334 Err(e) => {
335 println!("❌ 验证过程出错,正在回滚配置修改...");
337 config_manager.restore_backup(backup).await?;
338 return Err(e);
339 }
340 }
341
342 println!();
344 let should_install = if self.auto_install {
345 true
346 } else {
347 user_interface.confirm("🤔 是否立即安装此依赖?").await?
348 };
349
350 if should_install {
351 println!();
353 println!("📦 正在安装 {}...", service.name);
354
355 let install_pipeline = {
356 let mut container = context.container.lock().unwrap();
357 container.get_install_pipeline()?
358 };
359
360 match install_pipeline
361 .install_dependencies(&[dependency_spec])
362 .await
363 {
364 Ok(install_result) => {
365 println!(" ├─ 📦 缓存 proto 文件 ✅");
366 println!(" ├─ 🔒 更新锁文件 ✅");
367 println!(" └─ ✅ 安装完成");
368 println!();
369 println!("💡 建议: 运行 'actr gen' 生成最新代码");
370
371 Ok(CommandResult::Install(install_result))
372 }
373 Err(e) => {
374 eprintln!("❌ 安装失败: {e}");
375 Ok(CommandResult::Success(
376 "Dependency added but installation failed".to_string(),
377 ))
378 }
379 }
380 } else {
381 println!("✅ 依赖已添加到配置文件");
382 println!("💡 运行 'actr install' 来安装依赖");
383 Ok(CommandResult::Success(
384 "Dependency added to configuration".to_string(),
385 ))
386 }
387 }
388}
389
390impl Default for DiscoveryCommand {
391 fn default() -> Self {
392 Self::new(None, false, false)
393 }
394}
395
396#[cfg(test)]
397mod tests {
398 use super::*;
399
400 #[test]
401 fn test_create_service_filter() {
402 let cmd = DiscoveryCommand::new(Some("user-*".to_string()), false, false);
403 let filter = cmd.create_service_filter();
404
405 assert!(filter.is_some());
406 let filter = filter.unwrap();
407 assert_eq!(filter.name_pattern, Some("user-*".to_string()));
408 }
409
410 #[test]
411 fn test_create_service_filter_none() {
412 let cmd = DiscoveryCommand::new(None, false, false);
413 let filter = cmd.create_service_filter();
414
415 assert!(filter.is_none());
416 }
417
418 #[test]
419 fn test_required_components() {
420 let cmd = DiscoveryCommand::default();
421 let components = cmd.required_components();
422
423 assert!(components.contains(&ComponentType::ServiceDiscovery));
425 assert!(components.contains(&ComponentType::UserInterface));
426 assert!(components.contains(&ComponentType::ConfigManager));
427 assert!(components.contains(&ComponentType::DependencyResolver));
428 assert!(components.contains(&ComponentType::NetworkValidator));
429 assert!(components.contains(&ComponentType::FingerprintValidator));
430 assert!(components.contains(&ComponentType::CacheManager));
431 assert!(components.contains(&ComponentType::ProtoProcessor));
432 }
433}