1#![allow(non_snake_case)]
2
3pub mod AirMetrics;
12
13pub mod AirStatus;
14
15pub mod DownloadStream;
16
17pub mod DownloadStreamChunk;
18
19pub mod ExtendedFileInfo;
20
21pub mod FileInfo;
22
23pub mod FileResult;
24
25pub mod IndexInfo;
26
27pub mod ResourceUsage;
28
29pub mod UpdateInfo;
30
31use std::{collections::HashMap, sync::Arc};
32
33use tokio::sync::Mutex;
34use CommonLibrary::Error::CommonError::CommonError;
35#[cfg(feature = "AirIntegration")]
36use AirLibrary::Vine::Generated::air::air_service_client::AirServiceClient;
37use tonic::{Request, transport::Channel};
38
39use crate::dev_log;
40
41pub const DEFAULT_AIR_SERVER_ADDRESS:&str = "[::1]:50053";
49
50#[derive(Clone)]
55pub struct AirClient {
56 #[cfg(feature = "AirIntegration")]
57 client:Option<Arc<Mutex<AirServiceClient<Channel>>>>,
60
61 address:String,
63}
64
65impl AirClient {
66 pub async fn new(address:&str) -> Result<Self, CommonError> {
87 dev_log!("grpc", "[AirClient] Connecting to Air daemon at: {}", address);
88
89 #[cfg(feature = "AirIntegration")]
90 {
91 let endpoint = address.parse::<tonic::transport::Endpoint>().map_err(|e| {
92 dev_log!("grpc", "error: [AirClient] Failed to parse address '{}': {}", address, e);
93 CommonError::IPCError { Description:format!("Invalid address '{}': {}", address, e) }
94 })?;
95
96 let channel = endpoint.connect().await.map_err(|e| {
97 dev_log!("grpc", "error: [AirClient] Failed to connect to Air daemon: {}", e);
98 CommonError::IPCError { Description:format!("Connection failed: {}", e) }
99 })?;
100
101 dev_log!("grpc", "[AirClient] Successfully connected to Air daemon at: {}", address);
102
103 let client = Arc::new(Mutex::new(AirServiceClient::new(channel)));
104
105 Ok(Self { client:Some(client), address:address.to_string() })
106 }
107
108 #[cfg(not(feature = "AirIntegration"))]
109 {
110 dev_log!("grpc", "error: [AirClient] AirIntegration feature is not enabled");
111
112 Err(CommonError::FeatureNotAvailable { FeatureName:"AirIntegration".to_string() })
113 }
114 }
115
116 pub fn is_connected(&self) -> bool {
122 #[cfg(feature = "AirIntegration")]
123 {
124 self.client.is_some()
125 }
126
127 #[cfg(not(feature = "AirIntegration"))]
128 {
129 false
130 }
131 }
132
133 pub fn address(&self) -> &str { &self.address }
138
139 pub async fn authenticate(
155 &self,
156
157 request_id:String,
158
159 username:String,
160
161 password:String,
162
163 provider:String,
164 ) -> Result<String, CommonError> {
165 dev_log!(
166 "grpc",
167 "[AirClient] Authenticating user '{}' with provider '{}'",
168 username,
169 provider
170 );
171
172 #[cfg(feature = "AirIntegration")]
173 {
174 use AirLibrary::Vine::Generated::air::AuthenticationRequest;
175
176 let username_display = username.clone();
177
178 let request = AuthenticationRequest { request_id, username, password, provider };
179
180 let client = self
181 .client
182 .as_ref()
183 .ok_or_else(|| CommonError::IPCError { Description:"Air client not initialized".to_string() })?;
184
185 let mut client_guard = client.lock().await;
186
187 match client_guard.authenticate(Request::new(request)).await {
188 Ok(response) => {
189 let response = response.into_inner();
190
191 if response.success {
192 dev_log!("grpc", "[AirClient] Authentication successful for user '{}'", username_display);
193
194 Ok(response.token)
195 } else {
196 dev_log!(
197 "grpc",
198 "error: [AirClient] Authentication failed for user '{}': {}",
199 username_display,
200 response.error
201 );
202
203 Err(CommonError::AccessDenied { Reason:response.error })
204 }
205 },
206
207 Err(e) => {
208 dev_log!("grpc", "error: [AirClient] Authentication RPC error: {}", e);
209
210 Err(CommonError::IPCError { Description:format!("Authentication RPC error: {}", e) })
211 },
212 }
213 }
214
215 #[cfg(not(feature = "AirIntegration"))]
216 {
217 Err(CommonError::FeatureNotAvailable { FeatureName:"AirIntegration".to_string() })
218 }
219 }
220
221 pub async fn check_for_updates(
235 &self,
236
237 request_id:String,
238
239 current_version:String,
240
241 channel:String,
242 ) -> Result<UpdateInfo::Struct, CommonError> {
243 dev_log!("grpc", "[AirClient] Checking for updates for version '{}'", current_version);
244
245 #[cfg(feature = "AirIntegration")]
246 {
247 use AirLibrary::Vine::Generated::air::UpdateCheckRequest;
248
249 let request = UpdateCheckRequest { request_id, current_version, channel };
250
251 let client = self
252 .client
253 .as_ref()
254 .ok_or_else(|| CommonError::IPCError { Description:"Air client not initialized".to_string() })?;
255
256 let mut client_guard = client.lock().await;
257
258 match client_guard.check_for_updates(Request::new(request)).await {
259 Ok(response) => {
260 let response:AirLibrary::Vine::Generated::air::UpdateCheckResponse = response.into_inner();
261
262 dev_log!(
263 "grpc",
264 "[AirClient] Update check completed. Update available: {}",
265 response.update_available
266 );
267
268 Ok(UpdateInfo::Struct {
269 update_available:response.update_available,
270 version:response.version,
271 download_url:response.download_url,
272 release_notes:response.release_notes,
273 })
274 },
275
276 Err(e) => {
277 dev_log!("grpc", "error: [AirClient] Check for updates RPC error: {}", e);
278
279 Err(CommonError::IPCError { Description:format!("Check for updates RPC error: {}", e) })
280 },
281 }
282 }
283
284 #[cfg(not(feature = "AirIntegration"))]
285 {
286 Err(CommonError::FeatureNotAvailable { FeatureName:"AirIntegration".to_string() })
287 }
288 }
289
290 pub async fn download_update(
302 &self,
303
304 request_id:String,
305
306 url:String,
307
308 destination_path:String,
309
310 checksum:String,
311
312 headers:HashMap<String, String>,
313 ) -> Result<FileInfo::Struct, CommonError> {
314 dev_log!("grpc", "[AirClient] Downloading update from: {}", url);
315
316 #[cfg(feature = "AirIntegration")]
317 {
318 use AirLibrary::Vine::Generated::air::DownloadRequest;
319
320 let request = DownloadRequest { request_id, url, destination_path, checksum, headers };
321
322 let client = self
323 .client
324 .as_ref()
325 .ok_or_else(|| CommonError::IPCError { Description:"Air client not initialized".to_string() })?;
326
327 let mut client_guard = client.lock().await;
328
329 match client_guard.download_update(Request::new(request)).await {
330 Ok(response) => {
331 let response:AirLibrary::Vine::Generated::air::DownloadResponse = response.into_inner();
332
333 if response.success {
334 dev_log!("grpc", "[AirClient] Update downloaded successfully to: {}", response.file_path);
335
336 Ok(FileInfo::Struct {
337 file_path:response.file_path,
338 file_size:response.file_size,
339 checksum:response.checksum,
340 })
341 } else {
342 dev_log!("grpc", "error: [AirClient] Update download failed: {}", response.error);
343
344 Err(CommonError::IPCError { Description:response.error })
345 }
346 },
347
348 Err(e) => {
349 dev_log!("grpc", "error: [AirClient] Download update RPC error: {}", e);
350
351 Err(CommonError::IPCError { Description:format!("Download update RPC error: {}", e) })
352 },
353 }
354 }
355
356 #[cfg(not(feature = "AirIntegration"))]
357 {
358 Err(CommonError::FeatureNotAvailable { FeatureName:"AirIntegration".to_string() })
359 }
360 }
361
362 pub async fn apply_update(&self, request_id:String, version:String, update_path:String) -> Result<(), CommonError> {
372 dev_log!("grpc", "[AirClient] Applying update version: {}", version);
373
374 #[cfg(feature = "AirIntegration")]
375 {
376 use AirLibrary::Vine::Generated::air::ApplyUpdateRequest;
377
378 let request = ApplyUpdateRequest { request_id, version, update_path };
379
380 let client = self
381 .client
382 .as_ref()
383 .ok_or_else(|| CommonError::IPCError { Description:"Air client not initialized".to_string() })?;
384
385 let mut client_guard = client.lock().await;
386
387 match client_guard.apply_update(Request::new(request)).await {
388 Ok(response) => {
389 let response:AirLibrary::Vine::Generated::air::ApplyUpdateResponse = response.into_inner();
390
391 if response.success {
392 dev_log!("grpc", "[AirClient] Update applied successfully");
393
394 Ok(())
395 } else {
396 dev_log!("grpc", "error: [AirClient] Update application failed: {}", response.error);
397
398 Err(CommonError::IPCError { Description:response.error })
399 }
400 },
401
402 Err(e) => {
403 dev_log!("grpc", "error: [AirClient] Apply update RPC error: {}", e);
404
405 Err(CommonError::IPCError { Description:format!("Apply update RPC error: {}", e) })
406 },
407 }
408 }
409
410 #[cfg(not(feature = "AirIntegration"))]
411 {
412 Err(CommonError::FeatureNotAvailable { FeatureName:"AirIntegration".to_string() })
413 }
414 }
415
416 pub async fn download_file(
432 &self,
433
434 request_id:String,
435
436 url:String,
437
438 destination_path:String,
439
440 checksum:String,
441
442 headers:HashMap<String, String>,
443 ) -> Result<FileInfo::Struct, CommonError> {
444 dev_log!("grpc", "[AirClient] Downloading file from: {}", url);
445
446 #[cfg(feature = "AirIntegration")]
447 {
448 use AirLibrary::Vine::Generated::air::DownloadRequest;
449
450 let request = DownloadRequest { request_id, url, destination_path, checksum, headers };
451
452 let client = self
453 .client
454 .as_ref()
455 .ok_or_else(|| CommonError::IPCError { Description:"Air client not initialized".to_string() })?;
456
457 let mut client_guard = client.lock().await;
458
459 match client_guard.download_file(Request::new(request)).await {
460 Ok(response) => {
461 let response:AirLibrary::Vine::Generated::air::DownloadResponse = response.into_inner();
462
463 if response.success {
464 dev_log!("grpc", "[AirClient] File downloaded successfully to: {}", response.file_path);
465
466 Ok(FileInfo::Struct {
467 file_path:response.file_path,
468 file_size:response.file_size,
469 checksum:response.checksum,
470 })
471 } else {
472 dev_log!("grpc", "error: [AirClient] File download failed: {}", response.error);
473
474 Err(CommonError::IPCError { Description:response.error })
475 }
476 },
477
478 Err(e) => {
479 dev_log!("grpc", "error: [AirClient] Download file RPC error: {}", e);
480
481 Err(CommonError::IPCError { Description:format!("Download file RPC error: {}", e) })
482 },
483 }
484 }
485
486 #[cfg(not(feature = "AirIntegration"))]
487 {
488 Err(CommonError::FeatureNotAvailable { FeatureName:"AirIntegration".to_string() })
489 }
490 }
491
492 pub async fn download_stream(
546 &self,
547
548 request_id:String,
549
550 url:String,
551
552 headers:HashMap<String, String>,
553 ) -> Result<DownloadStream::Struct, CommonError> {
554 dev_log!("grpc", "[AirClient] Starting stream download from: {}", url);
555
556 #[cfg(feature = "AirIntegration")]
557 {
558 use AirLibrary::Vine::Generated::air::DownloadStreamRequest;
559
560 let request = DownloadStreamRequest { request_id, url, headers };
561
562 let client = self
563 .client
564 .as_ref()
565 .ok_or_else(|| CommonError::IPCError { Description:"Air client not initialized".to_string() })?;
566
567 let mut client_guard = client.lock().await;
568
569 match client_guard.download_stream(Request::new(request)).await {
570 Ok(response) => {
571 dev_log!("grpc", "[AirClient] Stream download initiated successfully");
572
573 Ok(DownloadStream::Struct::new(response.into_inner()))
574 },
575
576 Err(e) => {
577 dev_log!("grpc", "error: [AirClient] Download stream RPC error: {}", e);
578
579 Err(CommonError::IPCError { Description:format!("Download stream RPC error: {}", e) })
580 },
581 }
582 }
583
584 #[cfg(not(feature = "AirIntegration"))]
585 {
586 Err(CommonError::FeatureNotAvailable { FeatureName:"AirIntegration".to_string() })
587 }
588 }
589
590 pub async fn index_files(
606 &self,
607
608 request_id:String,
609
610 path:String,
611
612 patterns:Vec<String>,
613
614 exclude_patterns:Vec<String>,
615
616 max_depth:u32,
617 ) -> Result<IndexInfo::Struct, CommonError> {
618 dev_log!("grpc", "[AirClient] Indexing files in: {}", path);
619
620 #[cfg(feature = "AirIntegration")]
621 {
622 use AirLibrary::Vine::Generated::air::IndexRequest;
623
624 let request = IndexRequest { request_id, path, patterns, exclude_patterns, max_depth };
625
626 let client = self
627 .client
628 .as_ref()
629 .ok_or_else(|| CommonError::IPCError { Description:"Air client not initialized".to_string() })?;
630
631 let mut client_guard = client.lock().await;
632
633 match client_guard.index_files(Request::new(request)).await {
634 Ok(response) => {
635 let response = response.into_inner();
636
637 dev_log!(
639 "grpc",
640 "[AirClient] Files indexed: {} (total size: {} bytes)",
641 response.files_indexed,
642 response.total_size
643 );
644
645 Ok(IndexInfo::Struct { files_indexed:response.files_indexed, total_size:response.total_size })
646 },
647
648 Err(e) => {
649 dev_log!("grpc", "error: [AirClient] Index files RPC error: {}", e);
650
651 Err(CommonError::IPCError { Description:format!("Index files RPC error: {}", e) })
652 },
653 }
654 }
655
656 #[cfg(not(feature = "AirIntegration"))]
657 {
658 Err(CommonError::FeatureNotAvailable { FeatureName:"AirIntegration".to_string() })
659 }
660 }
661
662 pub async fn search_files(
673 &self,
674
675 request_id:String,
676
677 query:String,
678
679 path:String,
680
681 max_results:u32,
682 ) -> Result<Vec<FileResult::Struct>, CommonError> {
683 dev_log!("grpc", "[AirClient] Searching for files with query: '{}' in: {}", query, path);
684
685 #[cfg(feature = "AirIntegration")]
686 {
687 use AirLibrary::Vine::Generated::air::SearchRequest;
688
689 let request = SearchRequest { request_id, query, path, max_results };
690
691 let client = self
692 .client
693 .as_ref()
694 .ok_or_else(|| CommonError::IPCError { Description:"Air client not initialized".to_string() })?;
695
696 let mut client_guard = client.lock().await;
697
698 match client_guard.search_files(Request::new(request)).await {
699 Ok(_response) => {
700 dev_log!("grpc", "[AirClient] Search completed");
701
702 Ok(Vec::new())
704 },
705
706 Err(e) => {
707 dev_log!("grpc", "error: [AirClient] Search files RPC error: {}", e);
708
709 Err(CommonError::IPCError { Description:format!("Search files RPC error: {}", e) })
710 },
711 }
712 }
713
714 #[cfg(not(feature = "AirIntegration"))]
715 {
716 Err(CommonError::FeatureNotAvailable { FeatureName:"AirIntegration".to_string() })
717 }
718 }
719
720 pub async fn get_file_info(&self, request_id:String, path:String) -> Result<ExtendedFileInfo::Struct, CommonError> {
729 let path_display = path.clone();
730
731 dev_log!("grpc", "[AirClient] Getting file info for: {}", path);
732
733 #[cfg(feature = "AirIntegration")]
734 {
735 use AirLibrary::Vine::Generated::air::FileInfoRequest;
736
737 let request = FileInfoRequest { request_id, path };
738
739 let client = self
740 .client
741 .as_ref()
742 .ok_or_else(|| CommonError::IPCError { Description:"Air client not initialized".to_string() })?;
743
744 let mut client_guard = client.lock().await;
745
746 match client_guard.get_file_info(Request::new(request)).await {
747 Ok(response) => {
748 let response:AirLibrary::Vine::Generated::air::FileInfoResponse = response.into_inner();
749
750 dev_log!(
751 "grpc",
752 "[AirClient] File info retrieved for: {} (exists: {})",
753 path_display,
754 response.exists
755 );
756
757 Ok(ExtendedFileInfo::Struct {
758 exists:response.exists,
759 size:response.size,
760 mime_type:response.mime_type,
761 checksum:response.checksum,
762 modified_time:response.modified_time,
763 })
764 },
765
766 Err(e) => {
767 dev_log!("grpc", "error: [AirClient] Get file info RPC error: {}", e);
768
769 Err(CommonError::IPCError { Description:format!("Get file info RPC error: {}", e) })
770 },
771 }
772 }
773
774 #[cfg(not(feature = "AirIntegration"))]
775 {
776 Err(CommonError::FeatureNotAvailable { FeatureName:"AirIntegration".to_string() })
777 }
778 }
779
780 pub async fn get_status(&self, request_id:String) -> Result<AirStatus::Struct, CommonError> {
790 dev_log!("grpc", "[AirClient] Getting Air daemon status");
791
792 #[cfg(feature = "AirIntegration")]
793 {
794 use AirLibrary::Vine::Generated::air::StatusRequest;
795
796 let request = StatusRequest { request_id };
797
798 let client = self
799 .client
800 .as_ref()
801 .ok_or_else(|| CommonError::IPCError { Description:"Air client not initialized".to_string() })?;
802
803 let mut client_guard = client.lock().await;
804
805 match client_guard.get_status(Request::new(request)).await {
806 Ok(response) => {
807 let response:AirLibrary::Vine::Generated::air::StatusResponse = response.into_inner();
808
809 dev_log!(
810 "grpc",
811 "[AirClient] Status retrieved. Active requests: {}",
812 response.active_requests
813 );
814
815 Ok(AirStatus::Struct {
816 version:response.version,
817 uptime_seconds:response.uptime_seconds,
818 total_requests:response.total_requests,
819 successful_requests:response.successful_requests,
820 failed_requests:response.failed_requests,
821 average_response_time:response.average_response_time,
822 memory_usage_mb:response.memory_usage_mb,
823 cpu_usage_percent:response.cpu_usage_percent,
824 active_requests:response.active_requests,
825 })
826 },
827
828 Err(e) => {
829 dev_log!("grpc", "error: [AirClient] Get status RPC error: {}", e);
830
831 Err(CommonError::IPCError { Description:format!("Get status RPC error: {}", e) })
832 },
833 }
834 }
835
836 #[cfg(not(feature = "AirIntegration"))]
837 {
838 Err(CommonError::FeatureNotAvailable { FeatureName:"AirIntegration".to_string() })
839 }
840 }
841
842 pub async fn health_check(&self) -> Result<bool, CommonError> {
848 dev_log!("grpc", "[AirClient] Performing health check");
849
850 #[cfg(feature = "AirIntegration")]
851 {
852 use AirLibrary::Vine::Generated::air::HealthCheckRequest;
853
854 let request = HealthCheckRequest {};
855
856 let client = self
857 .client
858 .as_ref()
859 .ok_or_else(|| CommonError::IPCError { Description:"Air client not initialized".to_string() })?;
860
861 let mut client_guard = client.lock().await;
862
863 match client_guard.health_check(Request::new(request)).await {
864 Ok(response) => {
865 let response:AirLibrary::Vine::Generated::air::HealthCheckResponse = response.into_inner();
866
867 dev_log!("grpc", "[AirClient] Health check result: {}", response.healthy);
868
869 Ok(response.healthy)
870 },
871
872 Err(e) => {
873 dev_log!("grpc", "error: [AirClient] Health check RPC error: {}", e);
874
875 Err(CommonError::IPCError { Description:format!("Health check RPC error: {}", e) })
876 },
877 }
878 }
879
880 #[cfg(not(feature = "AirIntegration"))]
881 {
882 Ok(true)
885 }
886 }
887
888 pub async fn get_metrics(
898 &self,
899
900 request_id:String,
901
902 metric_type:Option<String>,
903 ) -> Result<AirMetrics::Struct, CommonError> {
904 dev_log!("grpc", "[AirClient] Getting metrics (type: {:?})", metric_type.as_deref());
905
906 #[cfg(feature = "AirIntegration")]
907 {
908 use AirLibrary::Vine::Generated::air::MetricsRequest;
909
910 let request = MetricsRequest { request_id, metric_type:metric_type.unwrap_or_default() };
911
912 let client = self
913 .client
914 .as_ref()
915 .ok_or_else(|| CommonError::IPCError { Description:"Air client not initialized".to_string() })?;
916
917 let mut client_guard = client.lock().await;
918
919 match client_guard.get_metrics(Request::new(request)).await {
920 Ok(response) => {
921 let response:AirLibrary::Vine::Generated::air::MetricsResponse = response.into_inner();
922
923 dev_log!("grpc", "[AirClient] Metrics retrieved");
924
925 let metrics = AirMetrics::Struct {
927 memory_usage_mb:response
928 .metrics
929 .get("memory_usage_mb")
930 .and_then(|s| s.parse::<f64>().ok())
931 .unwrap_or(0.0),
932
933 cpu_usage_percent:response
934 .metrics
935 .get("cpu_usage_percent")
936 .and_then(|s| s.parse::<f64>().ok())
937 .unwrap_or(0.0),
938
939 network_usage_mbps:response
940 .metrics
941 .get("network_usage_mbps")
942 .and_then(|s| s.parse::<f64>().ok())
943 .unwrap_or(0.0),
944
945 disk_usage_mb:response
946 .metrics
947 .get("disk_usage_mb")
948 .and_then(|s| s.parse::<f64>().ok())
949 .unwrap_or(0.0),
950
951 average_response_time:response
952 .metrics
953 .get("average_response_time")
954 .and_then(|s| s.parse::<f64>().ok())
955 .unwrap_or(0.0),
956 };
957
958 Ok(metrics)
959 },
960
961 Err(e) => {
962 dev_log!("grpc", "error: [AirClient] Get metrics RPC error: {}", e);
963
964 Err(CommonError::IPCError { Description:format!("Get metrics RPC error: {}", e) })
965 },
966 }
967 }
968
969 #[cfg(not(feature = "AirIntegration"))]
970 {
971 Err(CommonError::FeatureNotAvailable { FeatureName:"AirIntegration".to_string() })
972 }
973 }
974
975 pub async fn get_resource_usage(&self, request_id:String) -> Result<ResourceUsage::Struct, CommonError> {
988 dev_log!("grpc", "[AirClient] Getting resource usage");
989
990 #[cfg(feature = "AirIntegration")]
991 {
992 use AirLibrary::Vine::Generated::air::ResourceUsageRequest;
993
994 let request = ResourceUsageRequest { request_id };
995
996 let client = self
997 .client
998 .as_ref()
999 .ok_or_else(|| CommonError::IPCError { Description:"Air client not initialized".to_string() })?;
1000
1001 let mut client_guard = client.lock().await;
1002
1003 match client_guard.get_resource_usage(Request::new(request)).await {
1004 Ok(response) => {
1005 let response:AirLibrary::Vine::Generated::air::ResourceUsageResponse = response.into_inner();
1006
1007 dev_log!("grpc", "[AirClient] Resource usage retrieved");
1008
1009 Ok(ResourceUsage::Struct {
1010 memory_usage_mb:response.memory_usage_mb,
1011 cpu_usage_percent:response.cpu_usage_percent,
1012 disk_usage_mb:response.disk_usage_mb,
1013 network_usage_mbps:response.network_usage_mbps,
1014 thread_count:0, open_file_handles:0, })
1017 },
1018
1019 Err(e) => {
1020 dev_log!("grpc", "error: [AirClient] Get resource usage RPC error: {}", e);
1021
1022 Err(CommonError::IPCError { Description:format!("Get resource usage RPC error: {}", e) })
1023 },
1024 }
1025 }
1026
1027 #[cfg(not(feature = "AirIntegration"))]
1028 {
1029 Err(CommonError::FeatureNotAvailable { FeatureName:"AirIntegration".to_string() })
1030 }
1031 }
1032
1033 pub async fn set_resource_limits(
1045 &self,
1046
1047 request_id:String,
1048
1049 memory_limit_mb:u32,
1050
1051 cpu_limit_percent:u32,
1052
1053 disk_limit_mb:u32,
1054 ) -> Result<(), CommonError> {
1055 dev_log!(
1056 "grpc",
1057 "[AirClient] Setting resource limits: memory={}MB, cpu={}%, disk={}MB",
1058 memory_limit_mb,
1059 cpu_limit_percent,
1060 disk_limit_mb
1061 );
1062
1063 #[cfg(feature = "AirIntegration")]
1064 {
1065 use AirLibrary::Vine::Generated::air::ResourceLimitsRequest;
1066
1067 let request = ResourceLimitsRequest { request_id, memory_limit_mb, cpu_limit_percent, disk_limit_mb };
1068
1069 let client = self
1070 .client
1071 .as_ref()
1072 .ok_or_else(|| CommonError::IPCError { Description:"Air client not initialized".to_string() })?;
1073
1074 let mut client_guard = client.lock().await;
1075
1076 match client_guard.set_resource_limits(Request::new(request)).await {
1077 Ok(response) => {
1078 let response:AirLibrary::Vine::Generated::air::ResourceLimitsResponse = response.into_inner();
1079
1080 if response.success {
1081 dev_log!("grpc", "[AirClient] Resource limits set successfully");
1082
1083 Ok(())
1084 } else {
1085 dev_log!("grpc", "error: [AirClient] Failed to set resource limits: {}", response.error);
1086
1087 Err(CommonError::IPCError { Description:response.error })
1088 }
1089 },
1090
1091 Err(e) => {
1092 dev_log!("grpc", "error: [AirClient] Set resource limits RPC error: {}", e);
1093
1094 Err(CommonError::IPCError { Description:format!("Set resource limits RPC error: {}", e) })
1095 },
1096 }
1097 }
1098
1099 #[cfg(not(feature = "AirIntegration"))]
1100 {
1101 Err(CommonError::FeatureNotAvailable { FeatureName:"AirIntegration".to_string() })
1102 }
1103 }
1104
1105 pub async fn get_configuration(
1119 &self,
1120
1121 request_id:String,
1122
1123 section:String,
1124 ) -> Result<HashMap<String, String>, CommonError> {
1125 let section_display = section.clone();
1126
1127 dev_log!("grpc", "[AirClient] Getting configuration for section: {}", section);
1128
1129 #[cfg(feature = "AirIntegration")]
1130 {
1131 use AirLibrary::Vine::Generated::air::ConfigurationRequest;
1132
1133 let request = ConfigurationRequest { request_id, section };
1134
1135 let client = self
1136 .client
1137 .as_ref()
1138 .ok_or_else(|| CommonError::IPCError { Description:"Air client not initialized".to_string() })?;
1139
1140 let mut client_guard = client.lock().await;
1141
1142 match client_guard.get_configuration(Request::new(request)).await {
1143 Ok(response) => {
1144 let response:AirLibrary::Vine::Generated::air::ConfigurationResponse = response.into_inner();
1145
1146 dev_log!(
1147 "grpc",
1148 "[AirClient] Configuration retrieved for section: {} ({} keys)",
1149 section_display,
1150 response.configuration.len()
1151 );
1152
1153 Ok(response.configuration)
1154 },
1155
1156 Err(e) => {
1157 dev_log!("grpc", "error: [AirClient] Get configuration RPC error: {}", e);
1158
1159 Err(CommonError::IPCError { Description:format!("Get configuration RPC error: {}", e) })
1160 },
1161 }
1162 }
1163
1164 #[cfg(not(feature = "AirIntegration"))]
1165 {
1166 Err(CommonError::FeatureNotAvailable { FeatureName:"AirIntegration".to_string() })
1167 }
1168 }
1169
1170 pub async fn update_configuration(
1180 &self,
1181
1182 request_id:String,
1183
1184 section:String,
1185
1186 updates:HashMap<String, String>,
1187 ) -> Result<(), CommonError> {
1188 let section_display = section.clone();
1189
1190 dev_log!(
1191 "grpc",
1192 "[AirClient] Updating configuration for section: {} ({} keys)",
1193 section_display,
1194 updates.len()
1195 );
1196
1197 #[cfg(feature = "AirIntegration")]
1198 {
1199 use AirLibrary::Vine::Generated::air::UpdateConfigurationRequest;
1200
1201 let request = UpdateConfigurationRequest { request_id, section, updates };
1202
1203 let client = self
1204 .client
1205 .as_ref()
1206 .ok_or_else(|| CommonError::IPCError { Description:"Air client not initialized".to_string() })?;
1207
1208 let mut client_guard = client.lock().await;
1209
1210 match client_guard.update_configuration(Request::new(request)).await {
1211 Ok(response) => {
1212 let response:AirLibrary::Vine::Generated::air::UpdateConfigurationResponse = response.into_inner();
1213
1214 if response.success {
1215 dev_log!(
1216 "grpc",
1217 "[AirClient] Configuration updated successfully for section: {}",
1218 section_display
1219 );
1220
1221 Ok(())
1222 } else {
1223 dev_log!("grpc", "error: [AirClient] Failed to update configuration: {}", response.error);
1224
1225 Err(CommonError::IPCError { Description:response.error })
1226 }
1227 },
1228
1229 Err(e) => {
1230 dev_log!("grpc", "error: [AirClient] Update configuration RPC error: {}", e);
1231
1232 Err(CommonError::IPCError { Description:format!("Update configuration RPC error: {}", e) })
1233 },
1234 }
1235 }
1236
1237 #[cfg(not(feature = "AirIntegration"))]
1238 {
1239 Err(CommonError::FeatureNotAvailable { FeatureName:"AirIntegration".to_string() })
1240 }
1241 }
1242}
1243
1244impl std::fmt::Debug for AirClient {
1252 fn fmt(&self, f:&mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "AirClient({})", self.address) }
1253}
1254
1255#[allow(dead_code)]
1261trait IntoRequestExt {
1262 fn into_request(self) -> tonic::Request<Self>
1263 where
1264 Self: Sized, {
1265 tonic::Request::new(self)
1266 }
1267}
1268
1269impl<T> IntoRequestExt for T {}