Skip to main content

modo_session/
device.rs

1/// Parse a human-readable device name from User-Agent string.
2/// Returns format like "Chrome on macOS", "Safari on iPhone", etc.
3pub fn parse_device_name(user_agent: &str) -> String {
4    let browser = parse_browser(user_agent);
5    let os = parse_os(user_agent);
6    format!("{browser} on {os}")
7}
8
9/// Parse device type from User-Agent string.
10/// Returns "mobile", "tablet", or "desktop".
11pub fn parse_device_type(user_agent: &str) -> String {
12    let ua = user_agent.to_lowercase();
13    if ua.contains("tablet") || ua.contains("ipad") {
14        "tablet".to_string()
15    } else if ua.contains("mobile")
16        || ua.contains("iphone")
17        || (ua.contains("android") && !ua.contains("tablet"))
18    {
19        "mobile".to_string()
20    } else {
21        "desktop".to_string()
22    }
23}
24
25fn parse_browser(ua: &str) -> &str {
26    if ua.contains("Edg/") {
27        "Edge"
28    } else if ua.contains("Firefox/") {
29        "Firefox"
30    } else if ua.contains("Chromium/") {
31        "Chromium"
32    } else if ua.contains("Chrome/") {
33        "Chrome"
34    } else if ua.contains("Safari/") {
35        "Safari"
36    } else {
37        "Unknown"
38    }
39}
40
41fn parse_os(ua: &str) -> &str {
42    if ua.contains("iPhone") {
43        "iPhone"
44    } else if ua.contains("iPad") {
45        "iPad"
46    } else if ua.contains("HarmonyOS") {
47        "HarmonyOS"
48    } else if ua.contains("Android") {
49        "Android"
50    } else if ua.contains("CrOS") {
51        "ChromeOS"
52    } else if ua.contains("Mac OS X") || ua.contains("Macintosh") || ua.contains("OS X") {
53        "macOS"
54    } else if ua.contains("Windows") {
55        "Windows"
56    } else if ua.contains("FreeBSD") {
57        "FreeBSD"
58    } else if ua.contains("OpenBSD") {
59        "OpenBSD"
60    } else if ua.contains("Linux") {
61        "Linux"
62    } else {
63        "Unknown"
64    }
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70
71    #[test]
72    fn chrome_on_macos() {
73        let ua = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";
74        assert_eq!(parse_device_name(ua), "Chrome on macOS");
75        assert_eq!(parse_device_type(ua), "desktop");
76    }
77
78    #[test]
79    fn safari_on_iphone() {
80        let ua = "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1";
81        assert_eq!(parse_device_name(ua), "Safari on iPhone");
82        assert_eq!(parse_device_type(ua), "mobile");
83    }
84
85    #[test]
86    fn firefox_on_windows() {
87        let ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:120.0) Gecko/20100101 Firefox/120.0";
88        assert_eq!(parse_device_name(ua), "Firefox on Windows");
89        assert_eq!(parse_device_type(ua), "desktop");
90    }
91
92    #[test]
93    fn edge_on_windows() {
94        let ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0";
95        assert_eq!(parse_device_name(ua), "Edge on Windows");
96        assert_eq!(parse_device_type(ua), "desktop");
97    }
98
99    #[test]
100    fn chrome_on_android_mobile() {
101        let ua = "Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36";
102        assert_eq!(parse_device_name(ua), "Chrome on Android");
103        assert_eq!(parse_device_type(ua), "mobile");
104    }
105
106    #[test]
107    fn safari_on_ipad() {
108        let ua = "Mozilla/5.0 (iPad; CPU OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1";
109        assert_eq!(parse_device_name(ua), "Safari on iPad");
110        assert_eq!(parse_device_type(ua), "tablet");
111    }
112
113    #[test]
114    fn chrome_on_linux() {
115        let ua = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";
116        assert_eq!(parse_device_name(ua), "Chrome on Linux");
117        assert_eq!(parse_device_type(ua), "desktop");
118    }
119
120    #[test]
121    fn unknown_ua() {
122        assert_eq!(parse_device_name("curl/7.88.1"), "Unknown on Unknown");
123        assert_eq!(parse_device_type("curl/7.88.1"), "desktop");
124    }
125
126    #[test]
127    fn empty_ua() {
128        assert_eq!(parse_device_name(""), "Unknown on Unknown");
129        assert_eq!(parse_device_type(""), "desktop");
130    }
131}