rws_proxy/traffic/
device.rs1#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
12pub struct Device {
13 pub browser: &'static str,
14 pub os: &'static str,
15 pub class: &'static str,
16}
17
18fn has(ua: &str, token: &str) -> bool {
19 ua.contains(token)
20}
21
22fn browser(ua: &str) -> &'static str {
25 const BROWSERS: &[(&str, &str)] = &[
26 ("Edg/", "Edge"),
27 ("EdgA/", "Edge"),
28 ("EdgiOS/", "Edge"),
29 ("OPR/", "Opera"),
30 ("OPiOS/", "Opera"),
31 ("Opera", "Opera"),
32 ("YaBrowser/", "Yandex"),
33 ("SamsungBrowser/", "Samsung Internet"),
34 ("UCBrowser/", "UC Browser"),
35 ("Vivaldi/", "Vivaldi"),
36 ("Brave", "Brave"),
37 ("DuckDuckGo/", "DuckDuckGo"),
38 ("FxiOS/", "Firefox"),
39 ("Firefox/", "Firefox"),
40 ("CriOS/", "Chrome"),
41 ("Chrome/", "Chrome"),
42 ("Chromium/", "Chrome"),
43 ("Safari/", "Safari"),
44 ];
45 BROWSERS.iter().find(|(token, _)| has(ua, token)).map_or("Other", |(_, name)| name)
46}
47
48fn os(ua: &str) -> &'static str {
49 if has(ua, "Windows") {
50 "Windows"
51 } else if has(ua, "iPhone") || has(ua, "iPad") || has(ua, "iPod") {
52 "iOS"
53 } else if has(ua, "Android") {
54 "Android"
55 } else if has(ua, "CrOS") {
56 "ChromeOS"
57 } else if has(ua, "Mac OS X") || has(ua, "Macintosh") {
58 "macOS"
59 } else if has(ua, "Linux") || has(ua, "X11") {
60 "Linux"
61 } else {
62 "Other"
63 }
64}
65
66fn class(ua: &str, os: &str) -> &'static str {
67 if has(ua, "iPad") || has(ua, "Tablet") || (os == "Android" && !has(ua, "Mobile")) {
68 "tablet"
69 } else if has(ua, "Mobi") || has(ua, "iPhone") || has(ua, "iPod") || os == "Android" {
70 "mobile"
71 } else {
72 "desktop"
73 }
74}
75
76fn os_from_hint(platform: &str) -> Option<&'static str> {
78 Some(match platform.trim().trim_matches('"') {
79 "Windows" => "Windows",
80 "macOS" => "macOS",
81 "Android" => "Android",
82 "iOS" => "iOS",
83 "Chrome OS" | "ChromeOS" => "ChromeOS",
84 "Linux" => "Linux",
85 "" => return None,
86 _ => "Other",
87 })
88}
89
90pub fn parse(user_agent: &str, platform_hint: Option<&str>, mobile_hint: Option<&str>) -> Device {
93 let os = platform_hint.and_then(os_from_hint).unwrap_or_else(|| os(user_agent));
94 let mut class = class(user_agent, os);
95 match mobile_hint.map(str::trim) {
97 Some("?1") => class = "mobile",
98 Some("?0") if class == "mobile" => class = "desktop",
99 _ => {}
100 }
101 Device { browser: browser(user_agent), os, class }
102}