actr_cli/commands/
check.rs1use 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 #[arg(value_name = "ACTR_URI")]
22 pub uris: Vec<String>,
23
24 #[arg(short = 'f', long = "file")]
26 pub config_file: Option<String>,
27
28 #[arg(short, long)]
30 pub verbose: bool,
31
32 #[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 let uris_to_check = if self.uris.is_empty() {
44 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 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 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 async fn load_uris_from_config(&self, config_path: &str) -> Result<Vec<String>> {
107 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 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 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 async fn check_actr_uri(&self, uri: &str) -> Result<bool> {
168 info!("đ Checking Actor-RTC service: {}", uri);
169
170 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 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..]; 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 async fn check_service_availability(&self, service_name: &str) -> Result<bool> {
214 debug!("Checking service availability: actr://{}/", service_name);
215
216 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#[derive(Debug, Clone)]
238enum ActrUri {
239 Service { service_name: String },
241}