Skip to main content

vize_musea/vrt/
preset.rs

1//! Viewport presets for VRT.
2//!
3//! Common device viewport configurations.
4
5use serde::{Deserialize, Serialize};
6use std::str::FromStr;
7
8use crate::types::ViewportConfig;
9
10/// Predefined viewport presets.
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
12#[serde(rename_all = "lowercase")]
13pub enum ViewportPreset {
14    // Desktop
15    Desktop,
16    DesktopHd,
17    Desktop4k,
18
19    // Tablet
20    TabletPortrait,
21    TabletLandscape,
22    IpadPro,
23
24    // Mobile
25    Mobile,
26    MobileLandscape,
27    IphoneSe,
28    Iphone14,
29    Iphone14Pro,
30    Iphone14ProMax,
31    Pixel7,
32
33    // Custom
34    Custom,
35}
36
37impl ViewportPreset {
38    /// Get the viewport configuration for this preset.
39    #[inline]
40    pub const fn config(&self) -> ViewportConfig {
41        match self {
42            // Desktop
43            Self::Desktop => ViewportConfig {
44                width: 1280,
45                height: 720,
46                device_scale_factor: Some(1.0),
47            },
48            Self::DesktopHd => ViewportConfig {
49                width: 1920,
50                height: 1080,
51                device_scale_factor: Some(1.0),
52            },
53            Self::Desktop4k => ViewportConfig {
54                width: 3840,
55                height: 2160,
56                device_scale_factor: Some(1.0),
57            },
58
59            // Tablet
60            Self::TabletPortrait => ViewportConfig {
61                width: 768,
62                height: 1024,
63                device_scale_factor: Some(2.0),
64            },
65            Self::TabletLandscape => ViewportConfig {
66                width: 1024,
67                height: 768,
68                device_scale_factor: Some(2.0),
69            },
70            Self::IpadPro => ViewportConfig {
71                width: 1024,
72                height: 1366,
73                device_scale_factor: Some(2.0),
74            },
75
76            // Mobile
77            Self::Mobile => ViewportConfig {
78                width: 375,
79                height: 667,
80                device_scale_factor: Some(2.0),
81            },
82            Self::MobileLandscape => ViewportConfig {
83                width: 667,
84                height: 375,
85                device_scale_factor: Some(2.0),
86            },
87            Self::IphoneSe => ViewportConfig {
88                width: 375,
89                height: 667,
90                device_scale_factor: Some(2.0),
91            },
92            Self::Iphone14 => ViewportConfig {
93                width: 390,
94                height: 844,
95                device_scale_factor: Some(3.0),
96            },
97            Self::Iphone14Pro => ViewportConfig {
98                width: 393,
99                height: 852,
100                device_scale_factor: Some(3.0),
101            },
102            Self::Iphone14ProMax => ViewportConfig {
103                width: 430,
104                height: 932,
105                device_scale_factor: Some(3.0),
106            },
107            Self::Pixel7 => ViewportConfig {
108                width: 412,
109                height: 915,
110                device_scale_factor: Some(2.625),
111            },
112
113            // Custom - returns desktop as fallback
114            Self::Custom => ViewportConfig {
115                width: 1280,
116                height: 720,
117                device_scale_factor: Some(1.0),
118            },
119        }
120    }
121
122    /// Get the name of this preset.
123    pub const fn name(&self) -> &'static str {
124        match self {
125            Self::Desktop => "desktop",
126            Self::DesktopHd => "desktop-hd",
127            Self::Desktop4k => "desktop-4k",
128            Self::TabletPortrait => "tablet-portrait",
129            Self::TabletLandscape => "tablet-landscape",
130            Self::IpadPro => "ipad-pro",
131            Self::Mobile => "mobile",
132            Self::MobileLandscape => "mobile-landscape",
133            Self::IphoneSe => "iphone-se",
134            Self::Iphone14 => "iphone-14",
135            Self::Iphone14Pro => "iphone-14-pro",
136            Self::Iphone14ProMax => "iphone-14-pro-max",
137            Self::Pixel7 => "pixel-7",
138            Self::Custom => "custom",
139        }
140    }
141}
142
143impl FromStr for ViewportPreset {
144    type Err = ();
145
146    fn from_str(s: &str) -> Result<Self, Self::Err> {
147        match s.to_lowercase().as_str() {
148            "desktop" => Ok(Self::Desktop),
149            "desktop-hd" | "desktophd" => Ok(Self::DesktopHd),
150            "desktop-4k" | "desktop4k" => Ok(Self::Desktop4k),
151            "tablet" | "tablet-portrait" => Ok(Self::TabletPortrait),
152            "tablet-landscape" => Ok(Self::TabletLandscape),
153            "ipad-pro" | "ipadpro" => Ok(Self::IpadPro),
154            "mobile" => Ok(Self::Mobile),
155            "mobile-landscape" => Ok(Self::MobileLandscape),
156            "iphone-se" | "iphonese" => Ok(Self::IphoneSe),
157            "iphone-14" | "iphone14" => Ok(Self::Iphone14),
158            "iphone-14-pro" | "iphone14pro" => Ok(Self::Iphone14Pro),
159            "iphone-14-pro-max" | "iphone14promax" => Ok(Self::Iphone14ProMax),
160            "pixel-7" | "pixel7" => Ok(Self::Pixel7),
161            _ => Err(()),
162        }
163    }
164}
165
166impl From<ViewportPreset> for ViewportConfig {
167    #[inline]
168    fn from(preset: ViewportPreset) -> Self {
169        preset.config()
170    }
171}
172
173/// All available viewport presets.
174pub const PRESET_VIEWPORTS: &[ViewportPreset] = &[
175    ViewportPreset::Desktop,
176    ViewportPreset::DesktopHd,
177    ViewportPreset::Desktop4k,
178    ViewportPreset::TabletPortrait,
179    ViewportPreset::TabletLandscape,
180    ViewportPreset::IpadPro,
181    ViewportPreset::Mobile,
182    ViewportPreset::MobileLandscape,
183    ViewportPreset::IphoneSe,
184    ViewportPreset::Iphone14,
185    ViewportPreset::Iphone14Pro,
186    ViewportPreset::Iphone14ProMax,
187    ViewportPreset::Pixel7,
188];
189
190#[cfg(test)]
191mod tests {
192    use super::{PRESET_VIEWPORTS, ViewportPreset};
193    use crate::types::ViewportConfig;
194    use std::str::FromStr;
195
196    #[test]
197    fn test_desktop_preset() {
198        let config = ViewportPreset::Desktop.config();
199        assert_eq!(config.width, 1280);
200        assert_eq!(config.height, 720);
201    }
202
203    #[test]
204    fn test_mobile_preset() {
205        let config = ViewportPreset::Mobile.config();
206        assert_eq!(config.width, 375);
207        assert_eq!(config.height, 667);
208    }
209
210    #[test]
211    fn test_preset_from_str() {
212        assert_eq!(
213            ViewportPreset::from_str("desktop"),
214            Ok(ViewportPreset::Desktop)
215        );
216        assert_eq!(
217            ViewportPreset::from_str("mobile"),
218            Ok(ViewportPreset::Mobile)
219        );
220        assert_eq!(
221            ViewportPreset::from_str("iphone-14-pro"),
222            Ok(ViewportPreset::Iphone14Pro)
223        );
224        assert_eq!(ViewportPreset::from_str("unknown"), Err(()));
225    }
226
227    #[test]
228    fn test_preset_into_config() {
229        let config: ViewportConfig = ViewportPreset::Iphone14.into();
230        assert_eq!(config.width, 390);
231        assert_eq!(config.height, 844);
232        assert_eq!(config.device_scale_factor, Some(3.0));
233    }
234
235    #[test]
236    fn test_preset_viewports_count() {
237        assert_eq!(PRESET_VIEWPORTS.len(), 13);
238    }
239}