Seregon/StratoSDK

StratoSDK is a framework with a declarative approach similar to Flutter/React, written and designed entirely for Rust.

Rust/27.3 KB/No license
crates/strato-ui-core/src/platform/wasm.rs
1use std::sync::OnceLock;
2 
3use woothee::parser::{Parser, WootheeResult};
4 
5use crate::platform::OperatingSystem;
6 
7static PARSED_USER_AGENT: OnceLock<Option<ParsedUserAgent>> = OnceLock::new();
8static PLATFORM: OnceLock<OperatingSystem> = OnceLock::new();
9 
10#[derive(Debug)]
11struct ParsedUserAgent {
12 os: String,
13 /// For macOS, the version number is probably incorrect as it is currently
14 /// capped at 10.15. See: https://bugs.webkit.org/show_bug.cgi?id=216593.
15 /// It's possible to get the correct version using the Client Hints API, but
16 /// this is currently only supported by Chrome: https://developer.mozilla.org/en-US/docs/Web/API/User-Agent_Client_Hints_API.
17 os_version: String,
18 browser: String,
19 browser_version: String,
20}
21 
22impl ParsedUserAgent {
23 /// Converts the result we get from parsing the user agent into a struct
24 /// with owned values.
25 fn from_woothee_result(result: &WootheeResult) -> Self {
26 ParsedUserAgent {
27 os: result.os.to_string(),
28 os_version: result.os_version.to_string(),
29 browser: result.name.to_string(),
30 browser_version: result.version.to_string(),
31 }
32 }
33}
34 
35fn parsed_user_agent() -> Option<&'static ParsedUserAgent> {
36 PARSED_USER_AGENT
37 .get_or_init(|| {
38 let Ok(user_agent) = gloo::utils::window().navigator().user_agent() else {
39 return None;
40 };
41 
42 let parser = Parser::new();
43 parser
44 .parse(user_agent.as_str())
45 .map(|result| ParsedUserAgent::from_woothee_result(&result))
46 })
47 .as_ref()
48}
49 
50/// Returns the current operating system by reading the user agent. If the user agent was not able
51/// to be read, [`OperatingSystem::Other`] is returned.
52///
53/// # Panics
54/// Panics if called before the app was attached to the DOM.
55pub(super) fn current_platform() -> OperatingSystem {
56 *PLATFORM.get_or_init(|| {
57 let Some(parsed_user_agent) = parsed_user_agent() else {
58 return OperatingSystem::Other(None);
59 };
60 
61 // Try to parse the user agent to determine the OS. _heavily_ inspired by
62 // https://github.com/mozilla-services/contile/blob/61da2719fa4586fc0b15fe7f47ebbc1586f28a47/src/web/user_agent.rs#L95-L105.
63 let os = parsed_user_agent.os.to_lowercase();
64 match os.as_str() {
65 _ if os.starts_with("windows") => OperatingSystem::Windows,
66 "mac osx" => OperatingSystem::Mac,
67 "linux" => OperatingSystem::Linux,
68 _ => OperatingSystem::Other(Some(&parsed_user_agent.os)),
69 }
70 })
71}
72 
73/// Returns the user agent provided by the browser. If the user agent was
74/// unable to be read, returns None.
75pub fn user_agent() -> Option<String> {
76 gloo::utils::window().navigator().user_agent().ok()
77}
78 
79/// Returns the version of the current operating system, parsed from the user
80/// agent. If the user agent was not able to be read, returns None.
81///
82/// Also returns None if the current operating system is macOS. The version
83/// reported to the user agent is capped at 10.15, meaning it is probably
84/// incorrect in most cases: https://bugs.webkit.org/show_bug.cgi?id=216593.
85pub fn current_os_version() -> Option<&'static str> {
86 if matches!(current_platform(), OperatingSystem::Mac) {
87 return None;
88 };
89 
90 parsed_user_agent().map(|ua| ua.os_version.as_str())
91}
92 
93/// Returns the name of the browser, parsed from the user agent. If the user
94/// agent was not able to be read, returns None.
95pub fn current_browser() -> Option<&'static str> {
96 parsed_user_agent().map(|ua| ua.browser.as_str())
97}
98 
99/// Returns the version of the current browser, parsed from the user agent.
100/// If the user agent was not able to be read, returns None.
101pub fn current_browser_version() -> Option<&'static str> {
102 parsed_user_agent().map(|ua| ua.browser_version.as_str())
103}
104