Skip to main content

ri/device/discovery/
providers.rs

1//! Copyright © 2025-2026 Wenze Wei. All Rights Reserved.
2//!
3//! This file is part of Ri.
4//! The Ri project belongs to the Dunimd Team.
5//!
6//! Licensed under the Apache License, Version 2.0 (the "License");
7//! You may not use this file except in compliance with the License.
8//! You may obtain a copy of the License at
9//!
10//!     http://www.apache.org/licenses/LICENSE-2.0
11//!
12//! Unless required by applicable law or agreed to in writing, software
13//! distributed under the License is distributed on an "AS IS" BASIS,
14//! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15//! See the License for the specific language governing permissions and
16//! limitations under the License.
17
18//! # Hardware Discovery Providers
19//!
20//! This module provides hardware discovery providers for different device types.
21//! Each provider implements a common interface to detect and enumerate hardware
22//! on the current platform.
23//!
24//! ## Architecture
25//!
26//! - **RiHardwareProvider**: Trait defining the provider interface
27//! - **CPUProvider**: Discovers CPU devices
28//! - **MemoryProvider**: Discovers memory devices
29//! - **StorageProvider**: Discovers storage devices
30//! - **NetworkProvider**: Discovers network devices
31//! - **GPUProvider**: Discovers GPU devices
32//! - **USBProvider**: Discovers USB devices
33//! - **ProviderRegistry**: Manages all available providers
34//!
35//! ## Usage
36//!
37//! ```rust,ignore
38//! use ri::device::discovery::providers::{ProviderRegistry, CPUProvider};
39//!
40//! let mut registry = ProviderRegistry::new();
41//! registry.register(Box::new(CPUProvider::new()));
42//!
43//! // Discover all CPU devices
44//! let cpus = registry.discover_devices("cpu").await;
45//! ```
46
47use async_trait::async_trait;
48use std::sync::Arc;
49use tokio::sync::RwLock;
50use super::super::core::{RiDevice, RiDeviceType, RiDeviceCapabilities};
51
52/// Sanitizes command output for safe use in device names and logs.
53///
54/// # Security
55///
56/// This function prevents:
57/// 1. Log injection attacks (removes control characters)
58/// 2. ANSI escape sequences (prevents terminal escape attacks)
59/// 3. Excessive whitespace (prevents log flooding)
60/// 4. Non-printable characters
61fn 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            // Allow printable ASCII characters
68            ' '..='~' => {
69                // Collapse multiple spaces into one
70                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            // Allow Unicode letters and numbers
81            c if c.is_alphanumeric() => {
82                result.push(c);
83                last_was_space = false;
84            }
85            // Skip control characters, ANSI sequences, etc.
86            _ => continue,
87        }
88    }
89    
90    // Trim and limit length
91    let trimmed = result.trim();
92    if trimmed.len() > 256 {
93        trimmed[..256].to_string()
94    } else {
95        trimmed.to_string()
96    }
97}
98
99/// Safely executes a system command and returns sanitized output.
100///
101/// # Security
102///
103/// This function:
104/// 1. Uses hardcoded command paths (no user input)
105/// 2. Sanitizes output to prevent injection
106/// 3. Handles errors gracefully
107/// 4. Logs command execution for audit
108fn 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
134/// Result type for hardware discovery
135pub type DiscoveryResult<T> = Result<T, String>;
136
137/// Trait for hardware discovery providers
138#[async_trait]
139pub trait RiHardwareProvider: Send + Sync {
140    /// Returns the provider name
141    fn name(&self) -> &str;
142
143    /// Returns the hardware categories this provider handles
144    fn categories(&self) -> Vec<HardwareCategory>;
145
146    /// Discovers devices of this type
147    async fn discover(&self, platform: &PlatformInfo) -> DiscoveryResult<Vec<RiDevice>>;
148
149    /// Returns the priority of this provider (lower = higher priority)
150    fn priority(&self) -> u32;
151
152    /// Checks if this provider is available on the current platform
153    fn is_available(&self, platform: &PlatformInfo) -> bool;
154}
155
156/// CPU Discovery Provider
157pub struct CPUProvider {
158    priority: u32,
159}
160
161impl CPUProvider {
162    /// Creates a new CPU provider
163    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        // Get CPU information based on platform
188        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                // Generic fallback for other platforms
200                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
216/// Memory Discovery Provider
217pub 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
273/// Storage Discovery Provider
274pub 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
330/// Network Discovery Provider
331pub 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
387/// GPU Discovery Provider
388pub 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        // GPUs are not available in WebAssembly
441        platform.platform_type != super::platform::PlatformType::WebAssembly
442    }
443}
444
445/// USB Discovery Provider
446pub 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                // No USB support on other platforms
487            }
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/// Provider Registry - manages all hardware discovery providers
503#[derive(Default)]
504pub struct ProviderRegistry {
505    providers: Arc<RwLock<Vec<Arc<dyn RiHardwareProvider>>>>,
506}
507
508impl ProviderRegistry {
509    /// Creates a new provider registry
510    pub fn new() -> Self {
511        Self {
512            providers: Arc::new(RwLock::new(Vec::new())),
513        }
514    }
515
516    /// Registers a provider
517    pub async fn register(&self, provider: Box<dyn RiHardwareProvider>) {
518        let mut providers = self.providers.write().await;
519        providers.push(Arc::from(provider));
520        // Sort by priority
521        providers.sort_by_key(|p| p.priority());
522    }
523
524    /// Registers the default set of providers
525    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    /// Discovers devices of a specific type
535    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    /// Discovers all available devices
556    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    /// Returns the number of registered providers
573    pub async fn provider_count(&self) -> usize {
574        self.providers.read().await.len()
575    }
576}
577
578/// Creates a default device with basic capabilities
579fn 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
588// Platform-specific discovery implementations
589
590async fn discover_linux_cpus(_platform: &PlatformInfo) -> DiscoveryResult<Vec<RiDevice>> {
591    let mut devices = Vec::with_capacity(1);
592
593    // Read CPU info from /proc/cpuinfo
594    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); // Memory will be set by memory provider
613
614            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    // Security: Use safe_command_output to sanitize command output
630    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    // Security: Use safe_command_output to sanitize command output
655    let model_name = safe_command_output("wmic", &["CPU", "Get", "Name", "/VALUE"])
656        .and_then(|output| {
657            // Parse the output safely
658            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    // Read /proc/mounts to find mounted filesystems
728    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    // Check for NVIDIA GPUs
928    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    // Check for AMD GPUs in sysfs
953    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}