1pub 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
9pub 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}