1use 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#[derive(Args, Debug)]
16#[command(
17 about = "Discover network services",
18 long_about = "发现网络中的 Actor 服务,可以查看可用服务并选择安装"
19)]
20pub struct DiscoveryCommand {
21 #[arg(long, value_name = "PATTERN")]
23 pub filter: Option<String>,
24
25 #[arg(long)]
27 pub verbose: bool,
28
29 #[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 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 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 self.display_services_table(&services);
60
61 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 self.display_service_details(selected_service).await?;
70
71 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 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 self.export_proto_files(selected_service, &service_discovery)
94 .await?;
95 Ok(CommandResult::Success("Proto files exported".to_string()))
96 }
97 2 => {
98 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 vec![
109 ComponentType::ServiceDiscovery, ComponentType::UserInterface, ComponentType::ConfigManager, ComponentType::DependencyResolver, ComponentType::NetworkValidator, ComponentType::FingerprintValidator, ComponentType::CacheManager, ComponentType::ProtoProcessor, ]
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 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 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 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 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 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 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 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 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 let backup = config_manager.backup_config().await?;
295
296 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 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 println!("❌ 依赖验证失败,正在回滚配置修改...");
329 config_manager.restore_backup(backup).await?;
330
331 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 println!(" ├─ 📋 服务存在性检查 ✅");
349 println!(" ├─ 🌐 网络连通性测试 ✅");
350 println!(" └─ 🔐 指纹完整性验证 ✅");
351
352 config_manager.remove_backup(backup).await?;
354 }
355 }
356 Err(e) => {
357 println!("❌ 验证过程出错,正在回滚配置修改...");
359 config_manager.restore_backup(backup).await?;
360 return Err(e);
361 }
362 }
363
364 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 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 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}