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