From dbb68257fcdc99ae85b1f9a2f575f9a83c5e6ec5 Mon Sep 17 00:00:00 2001 From: Quentin Gliech Date: Thu, 24 Apr 2025 12:36:41 +0200 Subject: [PATCH] Compile the user-agent regexes once --- crates/data-model/src/user_agent.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/crates/data-model/src/user_agent.rs b/crates/data-model/src/user_agent.rs index 2b02ebd48..2ac4b06bd 100644 --- a/crates/data-model/src/user_agent.rs +++ b/crates/data-model/src/user_agent.rs @@ -4,9 +4,18 @@ // SPDX-License-Identifier: AGPL-3.0-only // Please see LICENSE in the repository root for full details. +use std::sync::LazyLock; + use serde::Serialize; use woothee::{parser::Parser, woothee::VALUE_UNKNOWN}; +static CUSTOM_USER_AGENT_REGEX: LazyLock = LazyLock::new(|| { + regex::Regex::new(r"^(?P[^/]+)/(?P[^ ]+) \((?P.+)\)$").unwrap() +}); + +static ELECTRON_USER_AGENT_REGEX: LazyLock = + LazyLock::new(|| regex::Regex::new(r"(?m)\w+/[\w.]+").unwrap()); + #[derive(Debug, Serialize, Clone, PartialEq, Eq)] #[serde(rename_all = "snake_case")] pub enum DeviceType { @@ -37,10 +46,7 @@ impl std::ops::Deref for UserAgent { impl UserAgent { fn parse_custom(user_agent: &str) -> Option<(&str, &str, &str, &str, Option<&str>)> { - let regex = regex::Regex::new(r"^(?P[^/]+)/(?P[^ ]+) \((?P.+)\)$") - .unwrap(); - - let captures = regex.captures(user_agent)?; + let captures = CUSTOM_USER_AGENT_REGEX.captures(user_agent)?; let name = captures.name("name")?.as_str(); let version = captures.name("version")?.as_str(); let segments: Vec<&str> = captures @@ -73,9 +79,8 @@ impl UserAgent { } fn parse_electron(user_agent: &str) -> Option<(&str, &str)> { - let regex = regex::Regex::new(r"(?m)\w+/[\w.]+").unwrap(); let omit_keys = ["Mozilla", "AppleWebKit", "Chrome", "Electron", "Safari"]; - return regex + return ELECTRON_USER_AGENT_REGEX .find_iter(user_agent) .map(|caps| caps.as_str().split_once('/').unwrap()) .find(|pair| !omit_keys.contains(&pair.0));