#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub struct Device {
pub browser: &'static str,
pub os: &'static str,
pub class: &'static str,
}
fn has(ua: &str, token: &str) -> bool {
ua.contains(token)
}
fn browser(ua: &str) -> &'static str {
const BROWSERS: &[(&str, &str)] = &[
("Edg/", "Edge"),
("EdgA/", "Edge"),
("EdgiOS/", "Edge"),
("OPR/", "Opera"),
("OPiOS/", "Opera"),
("Opera", "Opera"),
("YaBrowser/", "Yandex"),
("SamsungBrowser/", "Samsung Internet"),
("UCBrowser/", "UC Browser"),
("Vivaldi/", "Vivaldi"),
("Brave", "Brave"),
("DuckDuckGo/", "DuckDuckGo"),
("FxiOS/", "Firefox"),
("Firefox/", "Firefox"),
("CriOS/", "Chrome"),
("Chrome/", "Chrome"),
("Chromium/", "Chrome"),
("Safari/", "Safari"),
];
BROWSERS.iter().find(|(token, _)| has(ua, token)).map_or("Other", |(_, name)| name)
}
fn os(ua: &str) -> &'static str {
if has(ua, "Windows") {
"Windows"
} else if has(ua, "iPhone") || has(ua, "iPad") || has(ua, "iPod") {
"iOS"
} else if has(ua, "Android") {
"Android"
} else if has(ua, "CrOS") {
"ChromeOS"
} else if has(ua, "Mac OS X") || has(ua, "Macintosh") {
"macOS"
} else if has(ua, "Linux") || has(ua, "X11") {
"Linux"
} else {
"Other"
}
}
fn class(ua: &str, os: &str) -> &'static str {
if has(ua, "iPad") || has(ua, "Tablet") || (os == "Android" && !has(ua, "Mobile")) {
"tablet"
} else if has(ua, "Mobi") || has(ua, "iPhone") || has(ua, "iPod") || os == "Android" {
"mobile"
} else {
"desktop"
}
}
fn os_from_hint(platform: &str) -> Option<&'static str> {
Some(match platform.trim().trim_matches('"') {
"Windows" => "Windows",
"macOS" => "macOS",
"Android" => "Android",
"iOS" => "iOS",
"Chrome OS" | "ChromeOS" => "ChromeOS",
"Linux" => "Linux",
"" => return None,
_ => "Other",
})
}
pub fn parse(user_agent: &str, platform_hint: Option<&str>, mobile_hint: Option<&str>) -> Device {
let os = platform_hint.and_then(os_from_hint).unwrap_or_else(|| os(user_agent));
let mut class = class(user_agent, os);
match mobile_hint.map(str::trim) {
Some("?1") => class = "mobile",
Some("?0") if class == "mobile" => class = "desktop",
_ => {}
}
Device { browser: browser(user_agent), os, class }
}