statsig_rust/evaluation/user_agent_parsing/
third_party_ua_parser.rs1use crate::{dyn_value, log_d, log_e, DynamicValue};
2use parking_lot::RwLock;
3use std::borrow::Cow;
4use std::sync::Arc;
5use uaparser::{Parser, UserAgentParser as ExtUserAgentParser};
6
7lazy_static::lazy_static! {
8 static ref PARSER: Arc<RwLock<Option<ExtUserAgentParser>>> = Arc::from(RwLock::from(None));
9}
10
11const TAG: &str = "ThirdPartyUserAgentParser";
12
13pub struct ThirdPartyUserAgentParser;
14
15impl ThirdPartyUserAgentParser {
16 pub fn get_value_from_user_agent(
17 field: &str,
18 user_agent: &str,
19 ) -> Result<Option<DynamicValue>, &'static str> {
20 let lock = PARSER
21 .try_read_for(std::time::Duration::from_secs(5))
22 .ok_or("lock_failure")?;
23 let parser = lock.as_ref().ok_or("parser_not_loaded")?;
24
25 fn get_json_version(
26 major: Option<Cow<str>>,
27 minor: Option<Cow<str>>,
28 patch: Option<Cow<str>>,
29 ) -> String {
30 let mut result = String::new();
31 result += &major.unwrap_or(Cow::Borrowed("0"));
32 result += ".";
33 result += &minor.unwrap_or(Cow::Borrowed("0"));
34 result += ".";
35 result += &patch.unwrap_or(Cow::Borrowed("0"));
36 result
37 }
38
39 let result = match field {
40 "os_name" | "osname" => {
41 let os = parser.parse_os(user_agent);
42 os.family.to_string()
43 }
44 "os_version" | "osversion" => {
45 let os = parser.parse_os(user_agent);
46 get_json_version(os.major, os.minor, os.patch)
47 }
48 "browser_name" | "browsername" => {
49 let user_agent = parser.parse_user_agent(user_agent);
50 user_agent.family.to_string()
51 }
52 "browser_version" | "browserversion" => {
53 let user_agent = parser.parse_user_agent(user_agent);
54 get_json_version(user_agent.major, user_agent.minor, user_agent.patch)
55 }
56 _ => return Ok(None),
57 };
58
59 Ok(Some(dyn_value!(result)))
60 }
61
62 pub fn load_parser() {
63 match PARSER.try_read_for(std::time::Duration::from_secs(5)) {
64 Some(lock) => {
65 if lock.is_some() {
66 log_d!(TAG, "Parser already loaded");
67 return;
68 }
69 }
70 None => {
71 log_e!(
72 TAG,
73 "Failed to acquire read lock on parser: Failed to lock PARSER"
74 );
75 return;
76 }
77 }
78
79 log_d!(TAG, "Loading User Agent Parser...");
80
81 let bytes = include_bytes!("../../../resources/ua_parser_regex_lite.yaml");
82 let parser = match ExtUserAgentParser::from_bytes(bytes) {
83 Ok(parser) => parser,
84 Err(e) => {
85 log_e!(TAG, "Failed to load parser: {}", e);
86 return;
87 }
88 };
89
90 match PARSER.try_write_for(std::time::Duration::from_secs(5)) {
91 Some(mut lock) => {
92 *lock = Some(parser);
93 log_d!(TAG, "User Agent Parser Successfully Loaded");
94 }
95 None => {
96 log_e!(
97 TAG,
98 "Failed to acquire write lock on parser: Failed to lock PARSER"
99 );
100 }
101 }
102 }
103}