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