statsig_rust/evaluation/user_agent_parsing/
ua_parser.rs1use std::sync::Arc;
2
3use crate::evaluation::dynamic_string::DynamicString;
4use crate::evaluation::user_agent_parsing::ParsedUserAgentValue;
5use crate::interned_string::InternedString;
6use crate::user::StatsigUserInternal;
7use crate::{log_w, unwrap_or_return, DynamicValue, StatsigOptions, StatsigUser};
8
9use super::first_party_ua_parser::FirstPartyUserAgentParser;
10use super::third_party_ua_parser::ThirdPartyUserAgentParser;
11
12lazy_static::lazy_static! {
13 static ref USER_AGENT_STRING: Option<DynamicString> = Some(DynamicString::from("userAgent".to_string()));
14}
15
16const TAG: &str = "UserAgentParser";
17const UNINITIALIZED_REASON: &str = "UAParserNotLoaded";
18
19pub struct UserAgentParser;
20
21fn get_first_party_ua_value(key: &str, ua: &str) -> Option<InternedString> {
22 FirstPartyUserAgentParser::get_value_from_user_agent(key, ua)
23 .and_then(|v| v.string_value.map(|s| s.value))
24}
25
26fn get_third_party_ua_value(key: &str, ua: &str) -> Option<InternedString> {
27 ThirdPartyUserAgentParser::get_value_from_user_agent(key, ua)
28 .ok()
29 .flatten()
30 .and_then(|dv| dv.string_value.map(|s| s.value))
31}
32
33impl UserAgentParser {
34 pub fn get_value_from_user_agent(
35 user: &StatsigUserInternal,
36 field: &Option<DynamicString>,
37 override_reason: &mut Option<&str>,
38 use_third_party_ua_parser: bool,
39 ) -> Option<DynamicValue> {
40 let field_lowered = match field {
41 Some(f) => f.lowercased_value.as_str(),
42 _ => return None,
43 };
44
45 let user_agent = user.get_user_value(&USER_AGENT_STRING)?.string_value()?;
46
47 if user_agent.len() > 1000 {
48 return None;
49 }
50
51 if use_third_party_ua_parser {
52 let result =
53 ThirdPartyUserAgentParser::get_value_from_user_agent(field_lowered, user_agent);
54
55 match result {
56 Ok(v) => v,
57 Err(_) => {
58 *override_reason = Some(UNINITIALIZED_REASON);
59 log_w!(TAG, "Failed to load UA Parser. Check StatsigOptions.disable_user_agent_parsing and or wait_for_user_agent_init");
60 None
61 }
62 }
63 } else {
64 FirstPartyUserAgentParser::get_value_from_user_agent(field_lowered, user_agent)
65 }
66 }
67
68 pub fn load_parser() {
69 ThirdPartyUserAgentParser::load_parser();
70 }
71
72 pub fn get_parsed_user_agent_value_for_user(
73 user: &StatsigUser,
74 options: &Arc<StatsigOptions>,
75 ) -> Option<ParsedUserAgentValue> {
76 let user_agent_str = unwrap_or_return!(user.get_user_agent(), None);
77 match options.use_third_party_ua_parser {
78 Some(true) => Some(ParsedUserAgentValue {
79 os_name: get_third_party_ua_value("os_name", user_agent_str),
80 os_version: get_third_party_ua_value("os_version", user_agent_str),
81 browser_name: get_third_party_ua_value("browser_name", user_agent_str),
82 browser_version: get_third_party_ua_value("browser_version", user_agent_str),
83 }),
84 _ => Some(ParsedUserAgentValue {
85 os_name: get_first_party_ua_value("os_name", user_agent_str),
86 os_version: get_first_party_ua_value("os_version", user_agent_str),
87 browser_name: get_first_party_ua_value("browser_name", user_agent_str),
88 browser_version: get_first_party_ua_value("browser_version", user_agent_str),
89 }),
90 }
91 }
92}