1use async_trait::async_trait;
48use std::sync::Arc;
49use tokio::sync::RwLock;
50use super::super::core::{RiDevice, RiDeviceType, RiDeviceCapabilities};
51
52fn sanitize_command_output(output: &str) -> String {
62 let mut result = String::with_capacity(output.len());
63 let mut last_was_space = false;
64
65 for c in output.chars() {
66 match c {
67 ' '..='~' => {
69 if c == ' ' {
71 if !last_was_space {
72 result.push(c);
73 }
74 last_was_space = true;
75 } else {
76 result.push(c);
77 last_was_space = false;
78 }
79 }
80 c if c.is_alphanumeric() => {
82 result.push(c);
83 last_was_space = false;
84 }
85 _ => continue,
87 }
88 }
89
90 let trimmed = result.trim();
92 if trimmed.len() > 256 {
93 trimmed[..256].to_string()
94 } else {
95 trimmed.to_string()
96 }
97}
98
99fn safe_command_output(command: &str, args: &[&str]) -> Option<String> {
109 log::debug!("[Ri.Device] Executing command: {} {:?}", command, args);
110
111 let output = std::process::Command::new(command)
112 .args(args)
113 .output()
114 .ok()?;
115
116 if !output.status.success() {
117 log::warn!(
118 "[Ri.Device] Command failed: {} (exit code: {:?})",
119 command,
120 output.status.code()
121 );
122 return None;
123 }
124
125 let raw_output = String::from_utf8_lossy(&output.stdout);
126 let sanitized = sanitize_command_output(&raw_output);
127
128 log::debug!("[Ri.Device] Command output: {}", sanitized);
129
130 Some(sanitized)
131}
132use super::platform::{PlatformInfo, HardwareCategory};
133
134pub type DiscoveryResult<T> = Result<T, String>;
136
137#[async_trait]
139pub trait RiHardwareProvider: Send + Sync {
140 fn name(&self) -> &str;
142
143 fn categories(&self) -> Vec<HardwareCategory>;
145
146 async fn discover(&self, platform: &PlatformInfo) -> DiscoveryResult<Vec<RiDevice>>;
148
149 fn priority(&self) -> u32;
151
152 fn is_available(&self, platform: &PlatformInfo) -> bool;
154}
155
156pub struct CPUProvider {
158 priority: u32,
159}
160
161impl CPUProvider {
162 pub fn new() -> Self {
164 Self { priority: 10 }
165 }
166}
167
168impl Default for CPUProvider {
169 fn default() -> Self {
170 Self::new()
171 }
172}
173
174#[async_trait]
175impl RiHardwareProvider for CPUProvider {
176 fn name(&self) -> &str {
177 "CPUProvider"
178 }
179
180 fn categories(&self) -> Vec<HardwareCategory> {
181 vec![HardwareCategory::CPU]
182 }
183
184 async fn discover(&self, platform: &PlatformInfo) -> DiscoveryResult<Vec<RiDevice>> {
185 let mut devices = Vec::with_capacity(1);
186
187 match platform.platform_type {
189 super::platform::PlatformType::Linux => {
190 devices.extend(discover_linux_cpus(platform).await?);
191 }
192 super::platform::PlatformType::MacOS => {
193 devices.extend(discover_macos_cpus(platform).await?);
194 }
195 super::platform::PlatformType::Windows => {
196 devices.extend(discover_windows_cpus(platform).await?);
197 }
198 _ => {
199 devices.extend(discover_generic_cpus(platform).await?);
201 }
202 }
203
204 Ok(devices)
205 }
206
207 fn priority(&self) -> u32 {
208 self.priority
209 }
210
211 fn is_available(&self, _platform: &PlatformInfo) -> bool {
212 true
213 }
214}
215
216pub struct MemoryProvider {
218 priority: u32,
219}
220
221impl MemoryProvider {
222 pub fn new() -> Self {
223 Self { priority: 20 }
224 }
225}
226
227impl Default for MemoryProvider {
228 fn default() -> Self {
229 Self::new()
230 }
231}
232
233#[async_trait]
234impl RiHardwareProvider for MemoryProvider {
235 fn name(&self) -> &str {
236 "MemoryProvider"
237 }
238
239 fn categories(&self) -> Vec<HardwareCategory> {
240 vec![HardwareCategory::Memory]
241 }
242
243 async fn discover(&self, platform: &PlatformInfo) -> DiscoveryResult<Vec<RiDevice>> {
244 let mut devices = Vec::with_capacity(1);
245
246 match platform.platform_type {
247 super::platform::PlatformType::Linux => {
248 devices.extend(discover_linux_memory(platform).await?);
249 }
250 super::platform::PlatformType::MacOS => {
251 devices.extend(discover_macos_memory(platform).await?);
252 }
253 super::platform::PlatformType::Windows => {
254 devices.extend(discover_windows_memory(platform).await?);
255 }
256 _ => {
257 devices.extend(discover_generic_memory(platform).await?);
258 }
259 }
260
261 Ok(devices)
262 }
263
264 fn priority(&self) -> u32 {
265 self.priority
266 }
267
268 fn is_available(&self, _platform: &PlatformInfo) -> bool {
269 true
270 }
271}
272
273pub struct StorageProvider {
275 priority: u32,
276}
277
278impl StorageProvider {
279 pub fn new() -> Self {
280 Self { priority: 30 }
281 }
282}
283
284impl Default for StorageProvider {
285 fn default() -> Self {
286 Self::new()
287 }
288}
289
290#[async_trait]
291impl RiHardwareProvider for StorageProvider {
292 fn name(&self) -> &str {
293 "StorageProvider"
294 }
295
296 fn categories(&self) -> Vec<HardwareCategory> {
297 vec![HardwareCategory::Storage]
298 }
299
300 async fn discover(&self, platform: &PlatformInfo) -> DiscoveryResult<Vec<RiDevice>> {
301 let mut devices = Vec::with_capacity(1);
302
303 match platform.platform_type {
304 super::platform::PlatformType::Linux => {
305 devices.extend(discover_linux_storage(platform).await?);
306 }
307 super::platform::PlatformType::MacOS => {
308 devices.extend(discover_macos_storage(platform).await?);
309 }
310 super::platform::PlatformType::Windows => {
311 devices.extend(discover_windows_storage(platform).await?);
312 }
313 _ => {
314 devices.extend(discover_generic_storage(platform).await?);
315 }
316 }
317
318 Ok(devices)
319 }
320
321 fn priority(&self) -> u32 {
322 self.priority
323 }
324
325 fn is_available(&self, _platform: &PlatformInfo) -> bool {
326 true
327 }
328}
329
330pub struct NetworkProvider {
332 priority: u32,
333}
334
335impl NetworkProvider {
336 pub fn new() -> Self {
337 Self { priority: 40 }
338 }
339}
340
341impl Default for NetworkProvider {
342 fn default() -> Self {
343 Self::new()
344 }
345}
346
347#[async_trait]
348impl RiHardwareProvider for NetworkProvider {
349 fn name(&self) -> &str {
350 "NetworkProvider"
351 }
352
353 fn categories(&self) -> Vec<HardwareCategory> {
354 vec![HardwareCategory::Network]
355 }
356
357 async fn discover(&self, platform: &PlatformInfo) -> DiscoveryResult<Vec<RiDevice>> {
358 let mut devices = Vec::with_capacity(1);
359
360 match platform.platform_type {
361 super::platform::PlatformType::Linux => {
362 devices.extend(discover_linux_network(platform).await?);
363 }
364 super::platform::PlatformType::MacOS => {
365 devices.extend(discover_macos_network(platform).await?);
366 }
367 super::platform::PlatformType::Windows => {
368 devices.extend(discover_windows_network(platform).await?);
369 }
370 _ => {
371 devices.extend(discover_generic_network(platform).await?);
372 }
373 }
374
375 Ok(devices)
376 }
377
378 fn priority(&self) -> u32 {
379 self.priority
380 }
381
382 fn is_available(&self, _platform: &PlatformInfo) -> bool {
383 true
384 }
385}
386
387pub struct GPUProvider {
389 priority: u32,
390}
391
392impl GPUProvider {
393 pub fn new() -> Self {
394 Self { priority: 25 }
395 }
396}
397
398impl Default for GPUProvider {
399 fn default() -> Self {
400 Self::new()
401 }
402}
403
404#[async_trait]
405impl RiHardwareProvider for GPUProvider {
406 fn name(&self) -> &str {
407 "GPUProvider"
408 }
409
410 fn categories(&self) -> Vec<HardwareCategory> {
411 vec![HardwareCategory::GPU]
412 }
413
414 async fn discover(&self, platform: &PlatformInfo) -> DiscoveryResult<Vec<RiDevice>> {
415 let mut devices = Vec::with_capacity(1);
416
417 match platform.platform_type {
418 super::platform::PlatformType::Linux => {
419 devices.extend(discover_linux_gpus(platform).await?);
420 }
421 super::platform::PlatformType::MacOS => {
422 devices.extend(discover_macos_gpus(platform).await?);
423 }
424 super::platform::PlatformType::Windows => {
425 devices.extend(discover_windows_gpus(platform).await?);
426 }
427 _ => {
428 devices.extend(discover_generic_gpus(platform).await?);
429 }
430 }
431
432 Ok(devices)
433 }
434
435 fn priority(&self) -> u32 {
436 self.priority
437 }
438
439 fn is_available(&self, platform: &PlatformInfo) -> bool {
440 platform.platform_type != super::platform::PlatformType::WebAssembly
442 }
443}
444
445pub struct USBProvider {
447 priority: u32,
448}
449
450impl USBProvider {
451 pub fn new() -> Self {
452 Self { priority: 50 }
453 }
454}
455
456impl Default for USBProvider {
457 fn default() -> Self {
458 Self::new()
459 }
460}
461
462#[async_trait]
463impl RiHardwareProvider for USBProvider {
464 fn name(&self) -> &str {
465 "USBProvider"
466 }
467
468 fn categories(&self) -> Vec<HardwareCategory> {
469 vec![HardwareCategory::USB]
470 }
471
472 async fn discover(&self, platform: &PlatformInfo) -> DiscoveryResult<Vec<RiDevice>> {
473 let mut devices = Vec::with_capacity(1);
474
475 match platform.platform_type {
476 super::platform::PlatformType::Linux => {
477 devices.extend(discover_linux_usb(platform).await?);
478 }
479 super::platform::PlatformType::MacOS => {
480 devices.extend(discover_macos_usb(platform).await?);
481 }
482 super::platform::PlatformType::Windows => {
483 devices.extend(discover_windows_usb(platform).await?);
484 }
485 _ => {
486 }
488 }
489
490 Ok(devices)
491 }
492
493 fn priority(&self) -> u32 {
494 self.priority
495 }
496
497 fn is_available(&self, platform: &PlatformInfo) -> bool {
498 platform.platform_type != super::platform::PlatformType::WebAssembly
499 }
500}
501
502#[derive(Default)]
504pub struct ProviderRegistry {
505 providers: Arc<RwLock<Vec<Arc<dyn RiHardwareProvider>>>>,
506}
507
508impl ProviderRegistry {
509 pub fn new() -> Self {
511 Self {
512 providers: Arc::new(RwLock::new(Vec::new())),
513 }
514 }
515
516 pub async fn register(&self, provider: Box<dyn RiHardwareProvider>) {
518 let mut providers = self.providers.write().await;
519 providers.push(Arc::from(provider));
520 providers.sort_by_key(|p| p.priority());
522 }
523
524 pub async fn register_defaults(&self) {
526 self.register(Box::new(CPUProvider::new())).await;
527 self.register(Box::new(MemoryProvider::new())).await;
528 self.register(Box::new(StorageProvider::new())).await;
529 self.register(Box::new(NetworkProvider::new())).await;
530 self.register(Box::new(GPUProvider::new())).await;
531 self.register(Box::new(USBProvider::new())).await;
532 }
533
534 pub async fn discover_devices(
536 &self,
537 category: &HardwareCategory,
538 platform: &PlatformInfo,
539 ) -> DiscoveryResult<Vec<RiDevice>> {
540 let providers = self.providers.read().await;
541 let mut all_devices = Vec::with_capacity(8);
542
543 for provider in providers.iter() {
544 if provider.categories().contains(category) && provider.is_available(platform) {
545 match provider.discover(platform).await {
546 Ok(devices) => all_devices.extend(devices),
547 Err(e) => tracing::warn!("Provider {} failed: {}", provider.name(), e),
548 }
549 }
550 }
551
552 Ok(all_devices)
553 }
554
555 pub async fn discover_all(&self, platform: &PlatformInfo) -> DiscoveryResult<Vec<RiDevice>> {
557 let providers = self.providers.read().await;
558 let mut all_devices = Vec::with_capacity(8);
559
560 for provider in providers.iter() {
561 if provider.is_available(platform) {
562 match provider.discover(platform).await {
563 Ok(devices) => all_devices.extend(devices),
564 Err(e) => tracing::warn!("Provider {} failed: {}", provider.name(), e),
565 }
566 }
567 }
568
569 Ok(all_devices)
570 }
571
572 pub async fn provider_count(&self) -> usize {
574 self.providers.read().await.len()
575 }
576}
577
578fn create_device(
580 name: String,
581 device_type: RiDeviceType,
582 capabilities: RiDeviceCapabilities,
583) -> RiDevice {
584 RiDevice::new(name, device_type)
585 .with_capabilities(capabilities)
586}
587
588async fn discover_linux_cpus(_platform: &PlatformInfo) -> DiscoveryResult<Vec<RiDevice>> {
591 let mut devices = Vec::with_capacity(1);
592
593 if let Ok(content) = std::fs::read_to_string("/proc/cpuinfo") {
595 let mut core_count = 0;
596 let mut model_name = String::new();
597
598 for line in content.lines() {
599 if line.starts_with("processor") {
600 core_count += 1;
601 }
602 if line.starts_with("model name") || line.starts_with("Model name") {
603 if let Some(pos) = line.find(':') {
604 model_name = line[pos + 1..].trim().to_string();
605 }
606 }
607 }
608
609 if core_count > 0 {
610 let capabilities = RiDeviceCapabilities::new()
611 .with_compute_units(core_count)
612 .with_memory_gb(0.0); let device = create_device(
615 format!("CPU: {}", model_name),
616 RiDeviceType::CPU,
617 capabilities,
618 );
619 devices.push(device);
620 }
621 }
622
623 Ok(devices)
624}
625
626async fn discover_macos_cpus(_platform: &PlatformInfo) -> DiscoveryResult<Vec<RiDevice>> {
627 let mut devices = Vec::with_capacity(1);
628
629 let model_name = safe_command_output("sysctl", &["-n", "machdep.cpu.brand_string"])
631 .unwrap_or_else(|| "Unknown CPU".to_string());
632
633 let core_count = std::thread::available_parallelism()
634 .map(|p| p.get())
635 .unwrap_or(1);
636
637 let capabilities = RiDeviceCapabilities::new()
638 .with_compute_units(core_count)
639 .with_memory_gb(0.0);
640
641 let device = create_device(
642 format!("CPU: {}", model_name),
643 RiDeviceType::CPU,
644 capabilities,
645 );
646 devices.push(device);
647
648 Ok(devices)
649}
650
651async fn discover_windows_cpus(_platform: &PlatformInfo) -> DiscoveryResult<Vec<RiDevice>> {
652 let mut devices = Vec::with_capacity(1);
653
654 let model_name = safe_command_output("wmic", &["CPU", "Get", "Name", "/VALUE"])
656 .and_then(|output| {
657 if let Some(name_line) = output.lines().find(|l| l.starts_with("Name=")) {
659 Some(sanitize_command_output(&name_line[5..]))
660 } else {
661 None
662 }
663 })
664 .unwrap_or_else(|| "Unknown CPU".to_string());
665
666 let core_count = std::thread::available_parallelism()
667 .map(|p| p.get())
668 .unwrap_or(1);
669
670 let capabilities = RiDeviceCapabilities::new()
671 .with_compute_units(core_count)
672 .with_memory_gb(0.0);
673
674 let device = create_device(
675 format!("CPU: {}", model_name),
676 RiDeviceType::CPU,
677 capabilities,
678 );
679 devices.push(device);
680
681 Ok(devices)
682}
683
684async fn discover_generic_cpus(platform: &PlatformInfo) -> DiscoveryResult<Vec<RiDevice>> {
685 let capabilities = RiDeviceCapabilities::new()
686 .with_compute_units(platform.cpu_cores)
687 .with_memory_gb(0.0);
688
689 let device = create_device(
690 format!("Generic CPU ({} cores)", platform.cpu_cores),
691 RiDeviceType::CPU,
692 capabilities,
693 );
694
695 Ok(vec![device])
696}
697
698async fn discover_linux_memory(platform: &PlatformInfo) -> DiscoveryResult<Vec<RiDevice>> {
699 let capabilities = RiDeviceCapabilities::new()
700 .with_compute_units(0)
701 .with_memory_gb(platform.total_memory as f64 / (1024.0 * 1024.0 * 1024.0));
702
703 let device = create_device(
704 format!("System Memory ({:.2} GB)", platform.total_memory as f64 / (1024.0 * 1024.0 * 1024.0)),
705 RiDeviceType::Memory,
706 capabilities,
707 );
708
709 Ok(vec![device])
710}
711
712async fn discover_macos_memory(platform: &PlatformInfo) -> DiscoveryResult<Vec<RiDevice>> {
713 discover_linux_memory(platform).await
714}
715
716async fn discover_windows_memory(platform: &PlatformInfo) -> DiscoveryResult<Vec<RiDevice>> {
717 discover_linux_memory(platform).await
718}
719
720async fn discover_generic_memory(platform: &PlatformInfo) -> DiscoveryResult<Vec<RiDevice>> {
721 discover_linux_memory(platform).await
722}
723
724async fn discover_linux_storage(_platform: &PlatformInfo) -> DiscoveryResult<Vec<RiDevice>> {
725 let mut devices = Vec::with_capacity(1);
726
727 if let Ok(content) = std::fs::read_to_string("/proc/mounts") {
729 let mut seen = std::collections::HashSet::new();
730
731 for line in content.lines() {
732 let parts: Vec<&str> = line.split_whitespace().collect();
733 if parts.len() >= 2 {
734 let device = parts[0];
735 if device.starts_with("/dev/") && !seen.contains(device) {
736 seen.insert(device);
737
738 let capabilities = RiDeviceCapabilities::new()
739 .with_storage_gb(100.0);
740
741 let device_info = RiDevice::new(
742 device.to_string(),
743 RiDeviceType::Storage,
744 ).with_capabilities(capabilities);
745 devices.push(device_info);
746 }
747 }
748 }
749 }
750
751 Ok(devices)
752}
753
754async fn discover_macos_storage(_platform: &PlatformInfo) -> DiscoveryResult<Vec<RiDevice>> {
755 let mut devices = Vec::with_capacity(1);
756
757 let output = std::process::Command::new("df")
758 .arg("-l")
759 .output();
760
761 if let Ok(output) = output {
762 if output.status.success() {
763 let mut seen = std::collections::HashSet::new();
764 for line in String::from_utf8_lossy(&output.stdout).lines().skip(1) {
765 let parts: Vec<&str> = line.split_whitespace().collect();
766 if parts.len() >= 9 {
767 let device = parts[0];
768 if !seen.contains(device) && device.starts_with("/dev/") {
769 seen.insert(device);
770 let capabilities = RiDeviceCapabilities::new()
771 .with_storage_gb(100.0);
772
773 let device_info = RiDevice::new(
774 device.to_string(),
775 RiDeviceType::Storage,
776 ).with_capabilities(capabilities);
777 devices.push(device_info);
778 }
779 }
780 }
781 }
782 }
783
784 Ok(devices)
785}
786
787async fn discover_windows_storage(_platform: &PlatformInfo) -> DiscoveryResult<Vec<RiDevice>> {
788 let mut devices = Vec::with_capacity(1);
789
790 let output = std::process::Command::new("wmic")
791 .args(&["LogicalDisk", "Get", "Name,Size", "/VALUE"])
792 .output();
793
794 if let Ok(output) = output {
795 if output.status.success() {
796 for line in String::from_utf8_lossy(&output.stdout).lines() {
797 if line.starts_with("Name=") {
798 let drive = &line[5..];
799 let capabilities = RiDeviceCapabilities::new()
800 .with_storage_gb(100.0);
801
802 let device_info = RiDevice::new(
803 drive.to_string(),
804 RiDeviceType::Storage,
805 ).with_capabilities(capabilities);
806 devices.push(device_info);
807 }
808 }
809 }
810 }
811
812 Ok(devices)
813}
814
815async fn discover_generic_storage(_platform: &PlatformInfo) -> DiscoveryResult<Vec<RiDevice>> {
816 let capabilities = RiDeviceCapabilities::new()
817 .with_storage_gb(100.0);
818
819 let device = create_device(
820 "Generic Storage".to_string(),
821 RiDeviceType::Storage,
822 capabilities,
823 );
824
825 Ok(vec![device])
826}
827
828async fn discover_linux_network(_platform: &PlatformInfo) -> DiscoveryResult<Vec<RiDevice>> {
829 let mut devices = Vec::with_capacity(1);
830
831 if let Ok(content) = std::fs::read_to_string("/proc/net/dev") {
832 for line in content.lines().skip(2) {
833 let parts: Vec<&str> = line.split(':').collect();
834 if parts.len() >= 2 {
835 let interface = parts[0].trim();
836 if !interface.is_empty() && interface != "lo" {
837 let capabilities = RiDeviceCapabilities::new()
838 .with_bandwidth_gbps(1.0);
839
840 let device = RiDevice::new(
841 format!("Network Interface: {}", interface),
842 RiDeviceType::Network,
843 ).with_capabilities(capabilities);
844 devices.push(device);
845 }
846 }
847 }
848 }
849
850 Ok(devices)
851}
852
853async fn discover_macos_network(_platform: &PlatformInfo) -> DiscoveryResult<Vec<RiDevice>> {
854 let mut devices = Vec::with_capacity(1);
855
856 let output = std::process::Command::new("ifconfig")
857 .output();
858
859 if let Ok(output) = output {
860 for line in String::from_utf8_lossy(&output.stdout).lines() {
861 if line.starts_with_flags(&['a'..='z', 'A'..='Z']) && !line.starts_with("lo") {
862 let parts: Vec<&str> = line.split(':').collect();
863 if parts.len() >= 1 {
864 let interface = parts[0].trim();
865 if !interface.is_empty() {
866 let capabilities = RiDeviceCapabilities::new()
867 .with_bandwidth_gbps(1.0);
868
869 let device = RiDevice::new(
870 format!("Network Interface: {}", interface),
871 RiDeviceType::Network,
872 ).with_capabilities(capabilities);
873 devices.push(device);
874 }
875 }
876 }
877 }
878 }
879
880 Ok(devices)
881}
882
883async fn discover_windows_network(_platform: &PlatformInfo) -> DiscoveryResult<Vec<RiDevice>> {
884 let mut devices = Vec::with_capacity(1);
885
886 let output = std::process::Command::new("wmic")
887 .args(&["NicConfig", "Get", "Description,MACAddress", "/VALUE"])
888 .output();
889
890 if let Ok(output) = output {
891 if output.status.success() {
892 for line in String::from_utf8_lossy(&output.stdout).lines() {
893 if line.starts_with("Description=") {
894 let name = &line[12..];
895 let capabilities = RiDeviceCapabilities::new()
896 .with_bandwidth_gbps(1.0);
897
898 let device = RiDevice::new(
899 name.to_string(),
900 RiDeviceType::Network,
901 ).with_capabilities(capabilities);
902 devices.push(device);
903 }
904 }
905 }
906 }
907
908 Ok(devices)
909}
910
911async fn discover_generic_network(_platform: &PlatformInfo) -> DiscoveryResult<Vec<RiDevice>> {
912 let capabilities = RiDeviceCapabilities::new()
913 .with_bandwidth_gbps(1.0);
914
915 let device = create_device(
916 "Generic Network Adapter".to_string(),
917 RiDeviceType::Network,
918 capabilities,
919 );
920
921 Ok(vec![device])
922}
923
924async fn discover_linux_gpus(_platform: &PlatformInfo) -> DiscoveryResult<Vec<RiDevice>> {
925 let mut devices = Vec::with_capacity(1);
926
927 if let Ok(nvidia_output) = std::process::Command::new("nvidia-smi")
929 .arg("--query-gpu=name,memory.total,driver_version")
930 .arg("--format=csv,noheader")
931 .output()
932 {
933 if nvidia_output.status.success() {
934 for line in String::from_utf8_lossy(&nvidia_output.stdout).lines() {
935 let parts: Vec<&str> = line.split(',').collect();
936 if parts.len() >= 1 {
937 let name = parts[0].trim();
938 let capabilities = RiDeviceCapabilities::new()
939 .with_compute_units(1)
940 .with_memory_gb(8.0);
941
942 let device = RiDevice::new(
943 format!("NVIDIA GPU: {}", name),
944 RiDeviceType::GPU,
945 ).with_capabilities(capabilities);
946 devices.push(device);
947 }
948 }
949 }
950 }
951
952 if let Ok(amd_dirs) = std::fs::read_dir("/sys/class/drm") {
954 for entry in amd_dirs.flatten() {
955 if let Ok(path) = entry.path().join("device").read_link() {
956 if path.to_string_lossy().contains("pci") {
957 let capabilities = RiDeviceCapabilities::new()
958 .with_compute_units(1)
959 .with_memory_gb(4.0);
960
961 let device = RiDevice::new(
962 format!("GPU: {}", entry.file_name().to_string_lossy()),
963 RiDeviceType::GPU,
964 ).with_capabilities(capabilities);
965 devices.push(device);
966 }
967 }
968 }
969 }
970
971 Ok(devices)
972}
973
974async fn discover_macos_gpus(_platform: &PlatformInfo) -> DiscoveryResult<Vec<RiDevice>> {
975 let mut devices = Vec::with_capacity(1);
976
977 let output = std::process::Command::new("system_profiler")
978 .args(&["SPDisplaysDataType", "-detailLevel", "mini"])
979 .output();
980
981 if let Ok(output) = output {
982 if output.status.success() {
983 let content = String::from_utf8_lossy(&output.stdout);
984 let capabilities = RiDeviceCapabilities::new()
985 .with_compute_units(1)
986 .with_memory_gb(4.0);
987
988 let device = RiDevice::new(
989 format!("GPU: {}", content.lines().next().unwrap_or("Unknown")),
990 RiDeviceType::GPU,
991 ).with_capabilities(capabilities);
992 devices.push(device);
993 }
994 }
995
996 Ok(devices)
997}
998
999async fn discover_windows_gpus(_platform: &PlatformInfo) -> DiscoveryResult<Vec<RiDevice>> {
1000 let mut devices = Vec::with_capacity(1);
1001
1002 let output = std::process::Command::new("wmic")
1003 .args(&["Path", "Win32_VideoController", "Get", "Name,AdapterRAM", "/VALUE"])
1004 .output();
1005
1006 if let Ok(output) = output {
1007 if output.status.success() {
1008 for line in String::from_utf8_lossy(&output.stdout).lines() {
1009 if line.starts_with("Name=") {
1010 let name = &line[5..];
1011 let capabilities = RiDeviceCapabilities::new()
1012 .with_compute_units(1)
1013 .with_memory_gb(4.0);
1014
1015 let device = RiDevice::new(
1016 name.to_string(),
1017 RiDeviceType::GPU,
1018 ).with_capabilities(capabilities);
1019 devices.push(device);
1020 }
1021 }
1022 }
1023 }
1024
1025 Ok(devices)
1026}
1027
1028async fn discover_generic_gpus(_platform: &PlatformInfo) -> DiscoveryResult<Vec<RiDevice>> {
1029 let capabilities = RiDeviceCapabilities::new()
1030 .with_compute_units(1)
1031 .with_memory_gb(4.0);
1032
1033 let device = create_device(
1034 "Generic GPU".to_string(),
1035 RiDeviceType::GPU,
1036 capabilities,
1037 );
1038
1039 Ok(vec![device])
1040}
1041
1042async fn discover_linux_usb(_platform: &PlatformInfo) -> DiscoveryResult<Vec<RiDevice>> {
1043 let mut devices = Vec::with_capacity(1);
1044
1045 if let Ok(usb_dirs) = std::fs::read_dir("/sys/bus/usb/devices") {
1046 for entry in usb_dirs.flatten() {
1047 if let Ok(id) = entry.file_name().into_string() {
1048 if !id.is_empty() && !id.starts_with('.') {
1049 let capabilities = RiDeviceCapabilities::new();
1050
1051 let device = RiDevice::new(
1052 format!("USB Device: {}", id),
1053 RiDeviceType::Custom,
1054 ).with_capabilities(capabilities);
1055 devices.push(device);
1056 }
1057 }
1058 }
1059 }
1060
1061 Ok(devices)
1062}
1063
1064async fn discover_macos_usb(_platform: &PlatformInfo) -> DiscoveryResult<Vec<RiDevice>> {
1065 let mut devices = Vec::with_capacity(1);
1066
1067 let output = std::process::Command::new("system_profiler")
1068 .args(&["SPUSBDataType", "-detailLevel", "mini"])
1069 .output();
1070
1071 if let Ok(output) = output {
1072 if output.status.success() {
1073 let content = String::from_utf8_lossy(&output.stdout);
1074 let capabilities = RiDeviceCapabilities::new();
1075
1076 let device = RiDevice::new(
1077 format!("USB: {}", content.lines().next().unwrap_or("Unknown")),
1078 RiDeviceType::Custom,
1079 ).with_capabilities(capabilities);
1080 devices.push(device);
1081 }
1082 }
1083
1084 Ok(devices)
1085}
1086
1087async fn discover_windows_usb(_platform: &PlatformInfo) -> DiscoveryResult<Vec<RiDevice>> {
1088 let mut devices = Vec::with_capacity(1);
1089
1090 let output = std::process::Command::new("wmic")
1091 .args(&["USBController", "Get", "Name", "/VALUE"])
1092 .output();
1093
1094 if let Ok(output) = output {
1095 if output.status.success() {
1096 for line in String::from_utf8_lossy(&output.stdout).lines() {
1097 if line.starts_with("Name=") {
1098 let name = &line[5..];
1099 let capabilities = RiDeviceCapabilities::new();
1100
1101 let device = RiDevice::new(
1102 name.to_string(),
1103 RiDeviceType::Custom,
1104 ).with_capabilities(capabilities);
1105 devices.push(device);
1106 }
1107 }
1108 }
1109 }
1110
1111 Ok(devices)
1112}
1113
1114trait StartsWith {
1115 fn starts_with_flags(&self, ranges: &[std::ops::RangeInclusive<char>]) -> bool;
1116}
1117
1118impl StartsWith for str {
1119 fn starts_with_flags(&self, ranges: &[std::ops::RangeInclusive<char>]) -> bool {
1120 if let Some(first_char) = self.chars().next() {
1121 ranges.iter().any(|r| r.contains(&first_char))
1122 } else {
1123 false
1124 }
1125 }
1126}