1#![allow(non_snake_case)]
2
3pub mod AirMetrics;
12pub mod AirStatus;
13pub mod DownloadStream;
14pub mod DownloadStreamChunk;
15pub mod ExtendedFileInfo;
16pub mod FileInfo;
17pub mod FileResult;
18pub mod IndexInfo;
19pub mod ResourceUsage;
20pub mod UpdateInfo;
21
22use std::{collections::HashMap, sync::Arc};
23
24use tokio::sync::Mutex;
25use CommonLibrary::Error::CommonError::CommonError;
26#[cfg(feature = "AirIntegration")]
27use AirLibrary::Vine::Generated::air::air_service_client::AirServiceClient;
28use tonic::{Request, transport::Channel};
29
30use crate::dev_log;
31
32pub const DEFAULT_AIR_SERVER_ADDRESS:&str = "[::1]:50053";
40
41#[derive(Clone)]
46pub struct AirClient {
47 #[cfg(feature = "AirIntegration")]
48 client:Option<Arc<Mutex<AirServiceClient<Channel>>>>,
51 address:String,
53}
54
55impl AirClient {
56 pub async fn new(address:&str) -> Result<Self, CommonError> {
77 dev_log!("grpc", "[AirClient] Connecting to Air daemon at: {}", address);
78
79 #[cfg(feature = "AirIntegration")]
80 {
81 let endpoint = address.parse::<tonic::transport::Endpoint>().map_err(|e| {
82 dev_log!("grpc", "error: [AirClient] Failed to parse address '{}': {}", address, e);
83 CommonError::IPCError { Description:format!("Invalid address '{}': {}", address, e) }
84 })?;
85
86 let channel = endpoint.connect().await.map_err(|e| {
87 dev_log!("grpc", "error: [AirClient] Failed to connect to Air daemon: {}", e);
88 CommonError::IPCError { Description:format!("Connection failed: {}", e) }
89 })?;
90
91 dev_log!("grpc", "[AirClient] Successfully connected to Air daemon at: {}", address);
92
93 let client = Arc::new(Mutex::new(AirServiceClient::new(channel)));
94 Ok(Self { client:Some(client), address:address.to_string() })
95 }
96
97 #[cfg(not(feature = "AirIntegration"))]
98 {
99 dev_log!("grpc", "error: [AirClient] AirIntegration feature is not enabled");
100 Err(CommonError::FeatureNotAvailable { FeatureName:"AirIntegration".to_string() })
101 }
102 }
103
104 pub fn is_connected(&self) -> bool {
110 #[cfg(feature = "AirIntegration")]
111 {
112 self.client.is_some()
113 }
114
115 #[cfg(not(feature = "AirIntegration"))]
116 {
117 false
118 }
119 }
120
121 pub fn address(&self) -> &str { &self.address }
126
127 pub async fn authenticate(
143 &self,
144 request_id:String,
145 username:String,
146 password:String,
147 provider:String,
148 ) -> Result<String, CommonError> {
149 dev_log!(
150 "grpc",
151 "[AirClient] Authenticating user '{}' with provider '{}'",
152 username,
153 provider
154 );
155
156 #[cfg(feature = "AirIntegration")]
157 {
158 use AirLibrary::Vine::Generated::air::AuthenticationRequest;
159
160 let username_display = username.clone();
161 let request = AuthenticationRequest { request_id, username, password, provider };
162
163 let client = self
164 .client
165 .as_ref()
166 .ok_or_else(|| CommonError::IPCError { Description:"Air client not initialized".to_string() })?;
167
168 let mut client_guard = client.lock().await;
169 match client_guard.authenticate(Request::new(request)).await {
170 Ok(response) => {
171 let response = response.into_inner();
172 if response.success {
173 dev_log!("grpc", "[AirClient] Authentication successful for user '{}'", username_display);
174 Ok(response.token)
175 } else {
176 dev_log!(
177 "grpc",
178 "error: [AirClient] Authentication failed for user '{}': {}",
179 username_display,
180 response.error
181 );
182 Err(CommonError::AccessDenied { Reason:response.error })
183 }
184 },
185 Err(e) => {
186 dev_log!("grpc", "error: [AirClient] Authentication RPC error: {}", e);
187 Err(CommonError::IPCError { Description:format!("Authentication RPC error: {}", e) })
188 },
189 }
190 }
191
192 #[cfg(not(feature = "AirIntegration"))]
193 {
194 Err(CommonError::FeatureNotAvailable { FeatureName:"AirIntegration".to_string() })
195 }
196 }
197
198 pub async fn check_for_updates(
212 &self,
213 request_id:String,
214 current_version:String,
215 channel:String,
216 ) -> Result<UpdateInfo::Struct, CommonError> {
217 dev_log!("grpc", "[AirClient] Checking for updates for version '{}'", current_version);
218
219 #[cfg(feature = "AirIntegration")]
220 {
221 use AirLibrary::Vine::Generated::air::UpdateCheckRequest;
222
223 let request = UpdateCheckRequest { request_id, current_version, channel };
224
225 let client = self
226 .client
227 .as_ref()
228 .ok_or_else(|| CommonError::IPCError { Description:"Air client not initialized".to_string() })?;
229 let mut client_guard = client.lock().await;
230
231 match client_guard.check_for_updates(Request::new(request)).await {
232 Ok(response) => {
233 let response:AirLibrary::Vine::Generated::air::UpdateCheckResponse = response.into_inner();
234 dev_log!(
235 "grpc",
236 "[AirClient] Update check completed. Update available: {}",
237 response.update_available
238 );
239 Ok(UpdateInfo::Struct {
240 update_available:response.update_available,
241 version:response.version,
242 download_url:response.download_url,
243 release_notes:response.release_notes,
244 })
245 },
246 Err(e) => {
247 dev_log!("grpc", "error: [AirClient] Check for updates RPC error: {}", e);
248 Err(CommonError::IPCError { Description:format!("Check for updates RPC error: {}", e) })
249 },
250 }
251 }
252
253 #[cfg(not(feature = "AirIntegration"))]
254 {
255 Err(CommonError::FeatureNotAvailable { FeatureName:"AirIntegration".to_string() })
256 }
257 }
258
259 pub async fn download_update(
271 &self,
272 request_id:String,
273 url:String,
274 destination_path:String,
275 checksum:String,
276 headers:HashMap<String, String>,
277 ) -> Result<FileInfo::Struct, CommonError> {
278 dev_log!("grpc", "[AirClient] Downloading update from: {}", url);
279
280 #[cfg(feature = "AirIntegration")]
281 {
282 use AirLibrary::Vine::Generated::air::DownloadRequest;
283
284 let request = DownloadRequest { request_id, url, destination_path, checksum, headers };
285
286 let client = self
287 .client
288 .as_ref()
289 .ok_or_else(|| CommonError::IPCError { Description:"Air client not initialized".to_string() })?;
290 let mut client_guard = client.lock().await;
291
292 match client_guard.download_update(Request::new(request)).await {
293 Ok(response) => {
294 let response:AirLibrary::Vine::Generated::air::DownloadResponse = response.into_inner();
295 if response.success {
296 dev_log!("grpc", "[AirClient] Update downloaded successfully to: {}", response.file_path);
297 Ok(FileInfo::Struct {
298 file_path:response.file_path,
299 file_size:response.file_size,
300 checksum:response.checksum,
301 })
302 } else {
303 dev_log!("grpc", "error: [AirClient] Update download failed: {}", response.error);
304 Err(CommonError::IPCError { Description:response.error })
305 }
306 },
307 Err(e) => {
308 dev_log!("grpc", "error: [AirClient] Download update RPC error: {}", e);
309 Err(CommonError::IPCError { Description:format!("Download update RPC error: {}", e) })
310 },
311 }
312 }
313
314 #[cfg(not(feature = "AirIntegration"))]
315 {
316 Err(CommonError::FeatureNotAvailable { FeatureName:"AirIntegration".to_string() })
317 }
318 }
319
320 pub async fn apply_update(&self, request_id:String, version:String, update_path:String) -> Result<(), CommonError> {
330 dev_log!("grpc", "[AirClient] Applying update version: {}", version);
331
332 #[cfg(feature = "AirIntegration")]
333 {
334 use AirLibrary::Vine::Generated::air::ApplyUpdateRequest;
335
336 let request = ApplyUpdateRequest { request_id, version, update_path };
337
338 let client = self
339 .client
340 .as_ref()
341 .ok_or_else(|| CommonError::IPCError { Description:"Air client not initialized".to_string() })?;
342 let mut client_guard = client.lock().await;
343
344 match client_guard.apply_update(Request::new(request)).await {
345 Ok(response) => {
346 let response:AirLibrary::Vine::Generated::air::ApplyUpdateResponse = response.into_inner();
347 if response.success {
348 dev_log!("grpc", "[AirClient] Update applied successfully");
349 Ok(())
350 } else {
351 dev_log!("grpc", "error: [AirClient] Update application failed: {}", response.error);
352 Err(CommonError::IPCError { Description:response.error })
353 }
354 },
355 Err(e) => {
356 dev_log!("grpc", "error: [AirClient] Apply update RPC error: {}", e);
357 Err(CommonError::IPCError { Description:format!("Apply update RPC error: {}", e) })
358 },
359 }
360 }
361
362 #[cfg(not(feature = "AirIntegration"))]
363 {
364 Err(CommonError::FeatureNotAvailable { FeatureName:"AirIntegration".to_string() })
365 }
366 }
367
368 pub async fn download_file(
384 &self,
385 request_id:String,
386 url:String,
387 destination_path:String,
388 checksum:String,
389 headers:HashMap<String, String>,
390 ) -> Result<FileInfo::Struct, CommonError> {
391 dev_log!("grpc", "[AirClient] Downloading file from: {}", url);
392
393 #[cfg(feature = "AirIntegration")]
394 {
395 use AirLibrary::Vine::Generated::air::DownloadRequest;
396
397 let request = DownloadRequest { request_id, url, destination_path, checksum, headers };
398
399 let client = self
400 .client
401 .as_ref()
402 .ok_or_else(|| CommonError::IPCError { Description:"Air client not initialized".to_string() })?;
403 let mut client_guard = client.lock().await;
404
405 match client_guard.download_file(Request::new(request)).await {
406 Ok(response) => {
407 let response:AirLibrary::Vine::Generated::air::DownloadResponse = response.into_inner();
408 if response.success {
409 dev_log!("grpc", "[AirClient] File downloaded successfully to: {}", response.file_path);
410 Ok(FileInfo::Struct {
411 file_path:response.file_path,
412 file_size:response.file_size,
413 checksum:response.checksum,
414 })
415 } else {
416 dev_log!("grpc", "error: [AirClient] File download failed: {}", response.error);
417 Err(CommonError::IPCError { Description:response.error })
418 }
419 },
420 Err(e) => {
421 dev_log!("grpc", "error: [AirClient] Download file RPC error: {}", e);
422 Err(CommonError::IPCError { Description:format!("Download file RPC error: {}", e) })
423 },
424 }
425 }
426
427 #[cfg(not(feature = "AirIntegration"))]
428 {
429 Err(CommonError::FeatureNotAvailable { FeatureName:"AirIntegration".to_string() })
430 }
431 }
432
433 pub async fn download_stream(
487 &self,
488 request_id:String,
489 url:String,
490 headers:HashMap<String, String>,
491 ) -> Result<DownloadStream::Struct, CommonError> {
492 dev_log!("grpc", "[AirClient] Starting stream download from: {}", url);
493
494 #[cfg(feature = "AirIntegration")]
495 {
496 use AirLibrary::Vine::Generated::air::DownloadStreamRequest;
497
498 let request = DownloadStreamRequest { request_id, url, headers };
499
500 let client = self
501 .client
502 .as_ref()
503 .ok_or_else(|| CommonError::IPCError { Description:"Air client not initialized".to_string() })?;
504 let mut client_guard = client.lock().await;
505
506 match client_guard.download_stream(Request::new(request)).await {
507 Ok(response) => {
508 dev_log!("grpc", "[AirClient] Stream download initiated successfully");
509 Ok(DownloadStream::Struct::new(response.into_inner()))
510 },
511 Err(e) => {
512 dev_log!("grpc", "error: [AirClient] Download stream RPC error: {}", e);
513 Err(CommonError::IPCError { Description:format!("Download stream RPC error: {}", e) })
514 },
515 }
516 }
517
518 #[cfg(not(feature = "AirIntegration"))]
519 {
520 Err(CommonError::FeatureNotAvailable { FeatureName:"AirIntegration".to_string() })
521 }
522 }
523
524 pub async fn index_files(
540 &self,
541 request_id:String,
542 path:String,
543 patterns:Vec<String>,
544 exclude_patterns:Vec<String>,
545 max_depth:u32,
546 ) -> Result<IndexInfo::Struct, CommonError> {
547 dev_log!("grpc", "[AirClient] Indexing files in: {}", path);
548
549 #[cfg(feature = "AirIntegration")]
550 {
551 use AirLibrary::Vine::Generated::air::IndexRequest;
552
553 let request = IndexRequest { request_id, path, patterns, exclude_patterns, max_depth };
554
555 let client = self
556 .client
557 .as_ref()
558 .ok_or_else(|| CommonError::IPCError { Description:"Air client not initialized".to_string() })?;
559 let mut client_guard = client.lock().await;
560
561 match client_guard.index_files(Request::new(request)).await {
562 Ok(response) => {
563 let response = response.into_inner();
564 dev_log!(
566 "grpc",
567 "[AirClient] Files indexed: {} (total size: {} bytes)",
568 response.files_indexed,
569 response.total_size
570 );
571 Ok(IndexInfo::Struct { files_indexed:response.files_indexed, total_size:response.total_size })
572 },
573 Err(e) => {
574 dev_log!("grpc", "error: [AirClient] Index files RPC error: {}", e);
575 Err(CommonError::IPCError { Description:format!("Index files RPC error: {}", e) })
576 },
577 }
578 }
579
580 #[cfg(not(feature = "AirIntegration"))]
581 {
582 Err(CommonError::FeatureNotAvailable { FeatureName:"AirIntegration".to_string() })
583 }
584 }
585
586 pub async fn search_files(
597 &self,
598 request_id:String,
599 query:String,
600 path:String,
601 max_results:u32,
602 ) -> Result<Vec<FileResult::Struct>, CommonError> {
603 dev_log!("grpc", "[AirClient] Searching for files with query: '{}' in: {}", query, path);
604
605 #[cfg(feature = "AirIntegration")]
606 {
607 use AirLibrary::Vine::Generated::air::SearchRequest;
608
609 let request = SearchRequest { request_id, query, path, max_results };
610
611 let client = self
612 .client
613 .as_ref()
614 .ok_or_else(|| CommonError::IPCError { Description:"Air client not initialized".to_string() })?;
615 let mut client_guard = client.lock().await;
616
617 match client_guard.search_files(Request::new(request)).await {
618 Ok(_response) => {
619 dev_log!("grpc", "[AirClient] Search completed");
620 Ok(Vec::new())
622 },
623 Err(e) => {
624 dev_log!("grpc", "error: [AirClient] Search files RPC error: {}", e);
625 Err(CommonError::IPCError { Description:format!("Search files RPC error: {}", e) })
626 },
627 }
628 }
629
630 #[cfg(not(feature = "AirIntegration"))]
631 {
632 Err(CommonError::FeatureNotAvailable { FeatureName:"AirIntegration".to_string() })
633 }
634 }
635
636 pub async fn get_file_info(&self, request_id:String, path:String) -> Result<ExtendedFileInfo::Struct, CommonError> {
645 let path_display = path.clone();
646 dev_log!("grpc", "[AirClient] Getting file info for: {}", path);
647
648 #[cfg(feature = "AirIntegration")]
649 {
650 use AirLibrary::Vine::Generated::air::FileInfoRequest;
651
652 let request = FileInfoRequest { request_id, path };
653
654 let client = self
655 .client
656 .as_ref()
657 .ok_or_else(|| CommonError::IPCError { Description:"Air client not initialized".to_string() })?;
658 let mut client_guard = client.lock().await;
659
660 match client_guard.get_file_info(Request::new(request)).await {
661 Ok(response) => {
662 let response:AirLibrary::Vine::Generated::air::FileInfoResponse = response.into_inner();
663 dev_log!(
664 "grpc",
665 "[AirClient] File info retrieved for: {} (exists: {})",
666 path_display,
667 response.exists
668 );
669 Ok(ExtendedFileInfo::Struct {
670 exists:response.exists,
671 size:response.size,
672 mime_type:response.mime_type,
673 checksum:response.checksum,
674 modified_time:response.modified_time,
675 })
676 },
677 Err(e) => {
678 dev_log!("grpc", "error: [AirClient] Get file info RPC error: {}", e);
679 Err(CommonError::IPCError { Description:format!("Get file info RPC error: {}", e) })
680 },
681 }
682 }
683
684 #[cfg(not(feature = "AirIntegration"))]
685 {
686 Err(CommonError::FeatureNotAvailable { FeatureName:"AirIntegration".to_string() })
687 }
688 }
689
690 pub async fn get_status(&self, request_id:String) -> Result<AirStatus::Struct, CommonError> {
700 dev_log!("grpc", "[AirClient] Getting Air daemon status");
701
702 #[cfg(feature = "AirIntegration")]
703 {
704 use AirLibrary::Vine::Generated::air::StatusRequest;
705
706 let request = StatusRequest { request_id };
707
708 let client = self
709 .client
710 .as_ref()
711 .ok_or_else(|| CommonError::IPCError { Description:"Air client not initialized".to_string() })?;
712 let mut client_guard = client.lock().await;
713
714 match client_guard.get_status(Request::new(request)).await {
715 Ok(response) => {
716 let response:AirLibrary::Vine::Generated::air::StatusResponse = response.into_inner();
717 dev_log!(
718 "grpc",
719 "[AirClient] Status retrieved. Active requests: {}",
720 response.active_requests
721 );
722 Ok(AirStatus::Struct {
723 version:response.version,
724 uptime_seconds:response.uptime_seconds,
725 total_requests:response.total_requests,
726 successful_requests:response.successful_requests,
727 failed_requests:response.failed_requests,
728 average_response_time:response.average_response_time,
729 memory_usage_mb:response.memory_usage_mb,
730 cpu_usage_percent:response.cpu_usage_percent,
731 active_requests:response.active_requests,
732 })
733 },
734 Err(e) => {
735 dev_log!("grpc", "error: [AirClient] Get status RPC error: {}", e);
736 Err(CommonError::IPCError { Description:format!("Get status RPC error: {}", e) })
737 },
738 }
739 }
740
741 #[cfg(not(feature = "AirIntegration"))]
742 {
743 Err(CommonError::FeatureNotAvailable { FeatureName:"AirIntegration".to_string() })
744 }
745 }
746
747 pub async fn health_check(&self) -> Result<bool, CommonError> {
753 dev_log!("grpc", "[AirClient] Performing health check");
754
755 #[cfg(feature = "AirIntegration")]
756 {
757 use AirLibrary::Vine::Generated::air::HealthCheckRequest;
758
759 let request = HealthCheckRequest {};
760
761 let client = self
762 .client
763 .as_ref()
764 .ok_or_else(|| CommonError::IPCError { Description:"Air client not initialized".to_string() })?;
765 let mut client_guard = client.lock().await;
766
767 match client_guard.health_check(Request::new(request)).await {
768 Ok(response) => {
769 let response:AirLibrary::Vine::Generated::air::HealthCheckResponse = response.into_inner();
770 dev_log!("grpc", "[AirClient] Health check result: {}", response.healthy);
771 Ok(response.healthy)
772 },
773 Err(e) => {
774 dev_log!("grpc", "error: [AirClient] Health check RPC error: {}", e);
775 Err(CommonError::IPCError { Description:format!("Health check RPC error: {}", e) })
776 },
777 }
778 }
779
780 #[cfg(not(feature = "AirIntegration"))]
781 {
782 Ok(true)
785 }
786 }
787
788 pub async fn get_metrics(
798 &self,
799 request_id:String,
800 metric_type:Option<String>,
801 ) -> Result<AirMetrics::Struct, CommonError> {
802 dev_log!("grpc", "[AirClient] Getting metrics (type: {:?})", metric_type.as_deref());
803
804 #[cfg(feature = "AirIntegration")]
805 {
806 use AirLibrary::Vine::Generated::air::MetricsRequest;
807
808 let request = MetricsRequest { request_id, metric_type:metric_type.unwrap_or_default() };
809
810 let client = self
811 .client
812 .as_ref()
813 .ok_or_else(|| CommonError::IPCError { Description:"Air client not initialized".to_string() })?;
814 let mut client_guard = client.lock().await;
815
816 match client_guard.get_metrics(Request::new(request)).await {
817 Ok(response) => {
818 let response:AirLibrary::Vine::Generated::air::MetricsResponse = response.into_inner();
819 dev_log!("grpc", "[AirClient] Metrics retrieved");
820 let metrics = AirMetrics::Struct {
822 memory_usage_mb:response
823 .metrics
824 .get("memory_usage_mb")
825 .and_then(|s| s.parse::<f64>().ok())
826 .unwrap_or(0.0),
827 cpu_usage_percent:response
828 .metrics
829 .get("cpu_usage_percent")
830 .and_then(|s| s.parse::<f64>().ok())
831 .unwrap_or(0.0),
832 network_usage_mbps:response
833 .metrics
834 .get("network_usage_mbps")
835 .and_then(|s| s.parse::<f64>().ok())
836 .unwrap_or(0.0),
837 disk_usage_mb:response
838 .metrics
839 .get("disk_usage_mb")
840 .and_then(|s| s.parse::<f64>().ok())
841 .unwrap_or(0.0),
842 average_response_time:response
843 .metrics
844 .get("average_response_time")
845 .and_then(|s| s.parse::<f64>().ok())
846 .unwrap_or(0.0),
847 };
848 Ok(metrics)
849 },
850 Err(e) => {
851 dev_log!("grpc", "error: [AirClient] Get metrics RPC error: {}", e);
852 Err(CommonError::IPCError { Description:format!("Get metrics RPC error: {}", e) })
853 },
854 }
855 }
856
857 #[cfg(not(feature = "AirIntegration"))]
858 {
859 Err(CommonError::FeatureNotAvailable { FeatureName:"AirIntegration".to_string() })
860 }
861 }
862
863 pub async fn get_resource_usage(&self, request_id:String) -> Result<ResourceUsage::Struct, CommonError> {
876 dev_log!("grpc", "[AirClient] Getting resource usage");
877
878 #[cfg(feature = "AirIntegration")]
879 {
880 use AirLibrary::Vine::Generated::air::ResourceUsageRequest;
881
882 let request = ResourceUsageRequest { request_id };
883
884 let client = self
885 .client
886 .as_ref()
887 .ok_or_else(|| CommonError::IPCError { Description:"Air client not initialized".to_string() })?;
888 let mut client_guard = client.lock().await;
889
890 match client_guard.get_resource_usage(Request::new(request)).await {
891 Ok(response) => {
892 let response:AirLibrary::Vine::Generated::air::ResourceUsageResponse = response.into_inner();
893 dev_log!("grpc", "[AirClient] Resource usage retrieved");
894 Ok(ResourceUsage::Struct {
895 memory_usage_mb:response.memory_usage_mb,
896 cpu_usage_percent:response.cpu_usage_percent,
897 disk_usage_mb:response.disk_usage_mb,
898 network_usage_mbps:response.network_usage_mbps,
899 thread_count:0, open_file_handles:0, })
902 },
903 Err(e) => {
904 dev_log!("grpc", "error: [AirClient] Get resource usage RPC error: {}", e);
905 Err(CommonError::IPCError { Description:format!("Get resource usage RPC error: {}", e) })
906 },
907 }
908 }
909
910 #[cfg(not(feature = "AirIntegration"))]
911 {
912 Err(CommonError::FeatureNotAvailable { FeatureName:"AirIntegration".to_string() })
913 }
914 }
915
916 pub async fn set_resource_limits(
928 &self,
929 request_id:String,
930 memory_limit_mb:u32,
931 cpu_limit_percent:u32,
932 disk_limit_mb:u32,
933 ) -> Result<(), CommonError> {
934 dev_log!(
935 "grpc",
936 "[AirClient] Setting resource limits: memory={}MB, cpu={}%, disk={}MB",
937 memory_limit_mb,
938 cpu_limit_percent,
939 disk_limit_mb
940 );
941
942 #[cfg(feature = "AirIntegration")]
943 {
944 use AirLibrary::Vine::Generated::air::ResourceLimitsRequest;
945
946 let request = ResourceLimitsRequest { request_id, memory_limit_mb, cpu_limit_percent, disk_limit_mb };
947
948 let client = self
949 .client
950 .as_ref()
951 .ok_or_else(|| CommonError::IPCError { Description:"Air client not initialized".to_string() })?;
952 let mut client_guard = client.lock().await;
953
954 match client_guard.set_resource_limits(Request::new(request)).await {
955 Ok(response) => {
956 let response:AirLibrary::Vine::Generated::air::ResourceLimitsResponse = response.into_inner();
957 if response.success {
958 dev_log!("grpc", "[AirClient] Resource limits set successfully");
959 Ok(())
960 } else {
961 dev_log!("grpc", "error: [AirClient] Failed to set resource limits: {}", response.error);
962 Err(CommonError::IPCError { Description:response.error })
963 }
964 },
965 Err(e) => {
966 dev_log!("grpc", "error: [AirClient] Set resource limits RPC error: {}", e);
967 Err(CommonError::IPCError { Description:format!("Set resource limits RPC error: {}", e) })
968 },
969 }
970 }
971
972 #[cfg(not(feature = "AirIntegration"))]
973 {
974 Err(CommonError::FeatureNotAvailable { FeatureName:"AirIntegration".to_string() })
975 }
976 }
977
978 pub async fn get_configuration(
992 &self,
993 request_id:String,
994 section:String,
995 ) -> Result<HashMap<String, String>, CommonError> {
996 let section_display = section.clone();
997 dev_log!("grpc", "[AirClient] Getting configuration for section: {}", section);
998
999 #[cfg(feature = "AirIntegration")]
1000 {
1001 use AirLibrary::Vine::Generated::air::ConfigurationRequest;
1002
1003 let request = ConfigurationRequest { request_id, section };
1004
1005 let client = self
1006 .client
1007 .as_ref()
1008 .ok_or_else(|| CommonError::IPCError { Description:"Air client not initialized".to_string() })?;
1009 let mut client_guard = client.lock().await;
1010
1011 match client_guard.get_configuration(Request::new(request)).await {
1012 Ok(response) => {
1013 let response:AirLibrary::Vine::Generated::air::ConfigurationResponse = response.into_inner();
1014 dev_log!(
1015 "grpc",
1016 "[AirClient] Configuration retrieved for section: {} ({} keys)",
1017 section_display,
1018 response.configuration.len()
1019 );
1020 Ok(response.configuration)
1021 },
1022 Err(e) => {
1023 dev_log!("grpc", "error: [AirClient] Get configuration RPC error: {}", e);
1024 Err(CommonError::IPCError { Description:format!("Get configuration RPC error: {}", e) })
1025 },
1026 }
1027 }
1028
1029 #[cfg(not(feature = "AirIntegration"))]
1030 {
1031 Err(CommonError::FeatureNotAvailable { FeatureName:"AirIntegration".to_string() })
1032 }
1033 }
1034
1035 pub async fn update_configuration(
1045 &self,
1046 request_id:String,
1047 section:String,
1048 updates:HashMap<String, String>,
1049 ) -> Result<(), CommonError> {
1050 let section_display = section.clone();
1051 dev_log!(
1052 "grpc",
1053 "[AirClient] Updating configuration for section: {} ({} keys)",
1054 section_display,
1055 updates.len()
1056 );
1057
1058 #[cfg(feature = "AirIntegration")]
1059 {
1060 use AirLibrary::Vine::Generated::air::UpdateConfigurationRequest;
1061
1062 let request = UpdateConfigurationRequest { request_id, section, updates };
1063
1064 let client = self
1065 .client
1066 .as_ref()
1067 .ok_or_else(|| CommonError::IPCError { Description:"Air client not initialized".to_string() })?;
1068 let mut client_guard = client.lock().await;
1069
1070 match client_guard.update_configuration(Request::new(request)).await {
1071 Ok(response) => {
1072 let response:AirLibrary::Vine::Generated::air::UpdateConfigurationResponse = response.into_inner();
1073 if response.success {
1074 dev_log!(
1075 "grpc",
1076 "[AirClient] Configuration updated successfully for section: {}",
1077 section_display
1078 );
1079 Ok(())
1080 } else {
1081 dev_log!("grpc", "error: [AirClient] Failed to update configuration: {}", response.error);
1082 Err(CommonError::IPCError { Description:response.error })
1083 }
1084 },
1085 Err(e) => {
1086 dev_log!("grpc", "error: [AirClient] Update configuration RPC error: {}", e);
1087 Err(CommonError::IPCError { Description:format!("Update configuration RPC error: {}", e) })
1088 },
1089 }
1090 }
1091
1092 #[cfg(not(feature = "AirIntegration"))]
1093 {
1094 Err(CommonError::FeatureNotAvailable { FeatureName:"AirIntegration".to_string() })
1095 }
1096 }
1097}
1098
1099impl std::fmt::Debug for AirClient {
1107 fn fmt(&self, f:&mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "AirClient({})", self.address) }
1108}
1109
1110#[allow(dead_code)]
1116trait IntoRequestExt {
1117 fn into_request(self) -> tonic::Request<Self>
1118 where
1119 Self: Sized, {
1120 tonic::Request::new(self)
1121 }
1122}
1123
1124impl<T> IntoRequestExt for T {}