Skip to main content

v_shield/platform/
mod.rs

1//! 🛡️ V-Shield Platform Detection
2//!
3//! Detección automática de plataforma con config defaults.
4//!
5//! # Plataformas soportadas
6//!
7//! | Plataforma | `target_os` | Sync | Graphics |
8//! |------------|------------|------|----------|
9//! | Linux | `linux` | `std::sync` | X11/Wayland |
10//! | Windows | `windows` | `std::sync` | DirectX |
11//! | macOS | `macos` | `std::sync` | Metal |
12//! | Android | `android` | `std::sync` | EGL/OpenGL ES |
13//! | WASM | `wasm32` | `wasm_sync` | WebGL |
14//! | iOS | `ios` | `std::sync` | Metal |
15//!
16//! # Ejemplo
17//!
18//! ```rust
19//! use v_shield::platform::{Platform, current_platform};
20//!
21//! let p = current_platform();
22//! println!("Running on: {:?}", p);
23//! assert!(p.is_desktop() || p.is_mobile());
24//! ```
25
26/// Plataforma detectada
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub enum Platform {
29    /// Linux (Desktop)
30    Linux,
31    /// Windows (Desktop)
32    Windows,
33    /// macOS (Desktop)
34    MacOS,
35    /// Android (Mobile)
36    Android,
37    /// iOS (Mobile)
38    IOS,
39    /// WebAssembly (Browser)
40    Wasm,
41}
42
43impl Platform {
44    /// ¿Es desktop? (Linux, Windows, macOS)
45    pub fn is_desktop(self) -> bool {
46        matches!(self, Self::Linux | Self::Windows | Self::MacOS)
47    }
48
49    /// ¿Es mobile? (Android, iOS)
50    pub fn is_mobile(self) -> bool {
51        matches!(self, Self::Android | Self::IOS)
52    }
53
54    /// ¿Es WASM?
55    pub fn is_wasm(self) -> bool {
56        matches!(self, Self::Wasm)
57    }
58
59    /// ¿Soporta X11?
60    pub fn supports_x11(self) -> bool {
61        matches!(self, Self::Linux)
62    }
63
64    /// ¿Soporta Wayland?
65    pub fn supports_wayland(self) -> bool {
66        matches!(self, Self::Linux)
67    }
68
69    /// ¿Tiene GPU disponible? (aproximación)
70    pub fn has_gpu(self) -> bool {
71        !matches!(self, Self::Wasm)
72    }
73
74    /// Nombre legible
75    pub fn name(self) -> &'static str {
76        match self {
77            Self::Linux => "Linux",
78            Self::Windows => "Windows",
79            Self::MacOS => "macOS",
80            Self::Android => "Android",
81            Self::IOS => "iOS",
82            Self::Wasm => "WebAssembly",
83        }
84    }
85
86    /// Arquitectura del target
87    pub fn arch(self) -> &'static str {
88        if cfg!(target_arch = "x86_64") {
89            "x86_64"
90        } else if cfg!(target_arch = "aarch64") {
91            "aarch64"
92        } else if cfg!(target_arch = "arm") {
93            "arm"
94        } else if cfg!(target_arch = "wasm32") {
95            "wasm32"
96        } else {
97            "unknown"
98        }
99    }
100
101    /// Info completa para debug
102    pub fn debug_info(self) -> String {
103        format!(
104            "Platform: {} | Arch: {} | Desktop: {} | Mobile: {} | GPU: {}",
105            self.name(),
106            self.arch(),
107            self.is_desktop(),
108            self.is_mobile(),
109            self.has_gpu()
110        )
111    }
112}
113
114/// Detecta la plataforma actual en compile-time
115#[inline]
116pub const fn current_platform() -> Platform {
117    if cfg!(target_os = "linux") {
118        Platform::Linux
119    } else if cfg!(target_os = "windows") {
120        Platform::Windows
121    } else if cfg!(target_os = "macos") {
122        Platform::MacOS
123    } else if cfg!(target_os = "android") {
124        Platform::Android
125    } else if cfg!(target_os = "ios") {
126        Platform::IOS
127    } else if cfg!(target_arch = "wasm32") {
128        Platform::Wasm
129    } else {
130        // Fallback genérico
131        Platform::Linux
132    }
133}
134
135/// Configuración defaults por plataforma
136#[derive(Debug, Clone)]
137pub struct PlatformConfig {
138    /// ¿Usar VSync?
139    pub vsync: bool,
140    /// ¿Usar anti-aliasing?
141    pub antialiasing: bool,
142    /// Resolución por defecto (ancho)
143    pub default_width: u32,
144    /// Resolución por defecto (alto)
145    pub default_height: u32,
146    /// ¿Habilitar FSR auto?
147    pub fsr_auto: bool,
148    /// Umbral FPS para FSR auto
149    pub fsr_threshold: u32,
150}
151
152impl PlatformConfig {
153    /// Config defaults para la plataforma actual
154    pub fn for_current() -> Self {
155        let platform = current_platform();
156        match platform {
157            Platform::Linux => Self {
158                vsync: true,
159                antialiasing: false, // Termux-X11 no lo soporta bien
160                default_width: 1280,
161                default_height: 720,
162                fsr_auto: true,
163                fsr_threshold: 30,
164            },
165            Platform::Windows | Platform::MacOS => Self {
166                vsync: true,
167                antialiasing: true,
168                default_width: 1920,
169                default_height: 1080,
170                fsr_auto: false,
171                fsr_threshold: 60,
172            },
173            Platform::Android => Self {
174                vsync: true,
175                antialiasing: false,
176                default_width: 1280,
177                default_height: 720,
178                fsr_auto: true,
179                fsr_threshold: 25,
180            },
181            Platform::IOS => Self {
182                vsync: true,
183                antialiasing: false,
184                default_width: 1170,
185                default_height: 2532,
186                fsr_auto: true,
187                fsr_threshold: 30,
188            },
189            Platform::Wasm => Self {
190                vsync: true,
191                antialiasing: false,
192                default_width: 800,
193                default_height: 600,
194                fsr_auto: false,
195                fsr_threshold: 30,
196            },
197        }
198    }
199
200    /// Preset low-end (para hardware limitado)
201    pub fn low_end() -> Self {
202        Self {
203            vsync: false,
204            antialiasing: false,
205            default_width: 960,
206            default_height: 540,
207            fsr_auto: true,
208            fsr_threshold: 25,
209        }
210    }
211
212    /// Preset high-end (para hardware potente)
213    pub fn high_end() -> Self {
214        Self {
215            vsync: true,
216            antialiasing: true,
217            default_width: 2560,
218            default_height: 1440,
219            fsr_auto: false,
220            fsr_threshold: 120,
221        }
222    }
223}
224
225#[cfg(test)]
226mod tests {
227    use super::*;
228
229    #[test]
230    fn test_current_platform() {
231        let p = current_platform();
232        // En Termux/Android debería ser Android o Linux
233        // En desktop debería ser Linux/Windows/MacOS
234        assert!(p.is_desktop() || p.is_mobile() || p.is_wasm());
235    }
236
237    #[test]
238    fn test_platform_name() {
239        let p = current_platform();
240        let name = p.name();
241        assert!(!name.is_empty());
242        assert!(name.len() < 20);
243    }
244
245    #[test]
246    fn test_platform_config_defaults() {
247        let cfg = PlatformConfig::for_current();
248        assert!(cfg.vsync);
249        assert!(cfg.default_width > 0);
250        assert!(cfg.default_height > 0);
251    }
252
253    #[test]
254    fn test_platform_config_low_end() {
255        let cfg = PlatformConfig::low_end();
256        assert!(!cfg.vsync);
257        assert!(!cfg.antialiasing);
258        assert_eq!(cfg.default_width, 960);
259        assert_eq!(cfg.default_height, 540);
260        assert!(cfg.fsr_auto);
261    }
262
263    #[test]
264    fn test_platform_config_high_end() {
265        let cfg = PlatformConfig::high_end();
266        assert!(cfg.vsync);
267        assert!(cfg.antialiasing);
268        assert_eq!(cfg.default_width, 2560);
269        assert_eq!(cfg.default_height, 1440);
270    }
271
272    #[test]
273    fn test_platform_debug_info() {
274        let p = current_platform();
275        let info = p.debug_info();
276        assert!(info.contains("Platform:"));
277        assert!(info.contains("Arch:"));
278    }
279}