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