actr_cli/commands/
check.rs

1//! Check command implementation - verify Actor-RTC service connectivity
2//!
3//! The check command validates that Actor-RTC services are reachable and available
4//! in the current network environment using the actr:// service protocol.
5
6use crate::commands::Command;
7use crate::error::{ActrCliError, Result};
8use actr_config::ConfigParser;
9use async_trait::async_trait;
10use clap::Args;
11use tracing::{debug, error, info};
12
13#[derive(Args)]
14#[command(
15    about = "Validate project dependencies",
16    long_about = "Validate that Actor-RTC services are reachable and available in the current network environment"
17)]
18pub struct CheckCommand {
19    /// Actor-RTC service URIs to check (e.g., actr://user-service/, actr://order-service/)
20    /// If not provided, checks all actr:// service URIs from the configuration file
21    #[arg(value_name = "ACTR_URI")]
22    pub uris: Vec<String>,
23
24    /// Configuration file to load service URIs from (defaults to Actr.toml)
25    #[arg(short = 'f', long = "file")]
26    pub config_file: Option<String>,
27
28    /// Show detailed connection information
29    #[arg(short, long)]
30    pub verbose: bool,
31
32    /// Timeout for each service check in seconds
33    #[arg(long, default_value = "10")]
34    pub timeout: u64,
35}
36
37#[async_trait]
38impl Command for CheckCommand {
39    async fn execute(&self) -> Result<()> {
40        let config_path = self.config_file.as_deref().unwrap_or("Actr.toml");
41
42        // Determine which service URIs to check
43        let uris_to_check = if self.uris.is_empty() {
44            // Load service URIs from configuration file
45            info!(
46                "🔍 Loading Actor-RTC service URIs from configuration: {}",
47                config_path
48            );
49            self.load_uris_from_config(config_path).await?
50        } else {
51            // Use provided service URIs
52            info!("🔍 Checking provided Actor-RTC service URIs");
53            self.validate_provided_uris()?
54        };
55
56        if uris_to_check.is_empty() {
57            info!("â„šī¸ No Actor-RTC service URIs to check");
58            return Ok(());
59        }
60
61        info!(
62            "đŸ“Ļ Checking {} Actor-RTC service URIs...",
63            uris_to_check.len()
64        );
65
66        let mut total_checked = 0;
67        let mut available_count = 0;
68        let mut unavailable_count = 0;
69
70        for uri in &uris_to_check {
71            total_checked += 1;
72            let is_available = self.check_actr_uri(uri).await?;
73
74            if is_available {
75                available_count += 1;
76            } else {
77                unavailable_count += 1;
78            }
79        }
80
81        // Summary
82        info!("");
83        info!("📊 Actor-RTC Service Check Summary:");
84        info!("   Total checked: {}", total_checked);
85        info!("   ✅ Available: {}", available_count);
86        info!("   ❌ Unavailable: {}", unavailable_count);
87
88        if unavailable_count > 0 {
89            error!(
90                "âš ī¸ {} Actor-RTC services are not available in the current network",
91                unavailable_count
92            );
93            return Err(ActrCliError::dependency_error(
94                "Some Actor-RTC services are unavailable",
95            ));
96        } else {
97            info!("🎉 All Actor-RTC services are available and accessible!");
98        }
99
100        Ok(())
101    }
102}
103
104impl CheckCommand {
105    /// Load actr:// service URIs from configuration file
106    async fn load_uris_from_config(&self, config_path: &str) -> Result<Vec<String>> {
107        // Load configuration
108        let config = ConfigParser::from_file(config_path)
109            .map_err(|e| ActrCliError::config_error(format!("Failed to load config: {e}")))?;
110
111        let mut uris = Vec::new();
112
113        // Extract actr:// service URIs from dependencies
114        // Construct URI from ActrType: actr://<realm>:<manufacturer>+<name>@<version>/
115        for dependency in &config.dependencies {
116            let uri = format!(
117                "actr://{}:{}+{}@v1/",
118                dependency.realm.realm_id,
119                dependency.actr_type.manufacturer,
120                dependency.actr_type.name
121            );
122            uris.push(uri);
123            debug!(
124                "Added dependency URI: {} (alias: {})",
125                uris.last().unwrap(),
126                dependency.alias
127            );
128        }
129
130        if uris.is_empty() {
131            info!(
132                "â„šī¸ No dependencies found in configuration file: {}",
133                config_path
134            );
135        } else {
136            info!(
137                "📋 Found {} actr:// service URIs in configuration",
138                uris.len()
139            );
140        }
141
142        Ok(uris)
143    }
144
145    /// Validate provided service URIs and filter for actr:// protocol only
146    fn validate_provided_uris(&self) -> Result<Vec<String>> {
147        let mut valid_uris = Vec::new();
148
149        for uri in &self.uris {
150            if uri.starts_with("actr://") {
151                valid_uris.push(uri.clone());
152            } else {
153                error!(
154                    "❌ Invalid service URI protocol: {} (only actr:// service URIs are supported)",
155                    uri
156                );
157                return Err(ActrCliError::dependency_error(format!(
158                    "Invalid service URI protocol: {uri} (only actr:// service URIs are supported)"
159                )));
160            }
161        }
162
163        Ok(valid_uris)
164    }
165
166    /// Check availability of a specific actr:// service URI
167    async fn check_actr_uri(&self, uri: &str) -> Result<bool> {
168        info!("🔗 Checking Actor-RTC service: {}", uri);
169
170        // Parse the actr:// service URI
171        let service_uri = match self.parse_actr_uri(uri) {
172            Ok(parsed) => parsed,
173            Err(e) => {
174                error!("❌ [{}] Invalid Actor-RTC service URI format: {}", uri, e);
175                return Ok(false);
176            }
177        };
178
179        match service_uri {
180            ActrUri::Service { service_name } => {
181                self.check_service_availability(&service_name).await
182            }
183        }
184    }
185
186    /// Parse actr:// service URI into components
187    fn parse_actr_uri(&self, uri: &str) -> Result<ActrUri> {
188        if !uri.starts_with("actr://") {
189            return Err(ActrCliError::dependency_error(
190                "Service URI must start with actr://",
191            ));
192        }
193
194        let uri_part = &uri[7..]; // Remove "actr://"
195
196        // Service URI must end with / (service-level dependency)
197        if uri_part.ends_with('/') {
198            let service_name = uri_part.trim_end_matches('/').to_string();
199            if service_name.is_empty() {
200                return Err(ActrCliError::dependency_error(
201                    "Service name cannot be empty",
202                ));
203            }
204            return Ok(ActrUri::Service { service_name });
205        }
206
207        Err(ActrCliError::dependency_error(
208            "Invalid actr:// service URI format. Use 'actr://service-name/'",
209        ))
210    }
211
212    /// Check service-level availability (actr://service-name/)
213    async fn check_service_availability(&self, service_name: &str) -> Result<bool> {
214        debug!("Checking service availability: actr://{}/", service_name);
215
216        // TODO: Implement actual service discovery and connectivity check
217        // For now, just validate the URI format and return success
218        if service_name.is_empty() {
219            error!("❌ [actr://{}] Invalid empty service name", service_name);
220            return Ok(false);
221        }
222
223        if self.verbose {
224            info!(
225                "✅ [actr://{}] URI format is valid (service discovery not yet implemented)",
226                service_name
227            );
228        } else {
229            info!("✅ [actr://{}] URI format valid", service_name);
230        }
231
232        Ok(true)
233    }
234}
235
236/// Parsed Actor-RTC service URI components
237#[derive(Debug, Clone)]
238enum ActrUri {
239    /// Service-level URI: actr://service-name/
240    Service { service_name: String },
241}