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