actr_cli/commands/
discovery.rs

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