Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
479 changes: 308 additions & 171 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ edition = "2021"
clap = { version = "4.0", features = ["derive"], optional = true }
indexmap = { version = "2.0", features = ["serde"] }
serde = { version = "1.0", features = ["derive"] }
get-size = { version = "0.1.4", features = ["derive"] }
hyper = { version = "0.14", features = ["server", "tcp", "http1", "http2"], optional = true }
serde_yaml = "0.9"
serde_json = "1.0"
Expand All @@ -16,7 +17,7 @@ once_cell = "1.8"
tokio = { version = "1", features = ["full"], optional = true }
version-compare = "0.2.0"
fallible-iterator = "0.3"
moka = { version = "0.11", optional = true }
moka = { version = "0.12.8", features = ["future"], optional = true }
const_format = "0.2"
# dhat = "0.3.2"
libc = { version = "0.2", optional = true }
Expand All @@ -28,6 +29,7 @@ cbindgen = { version = "0.26", optional = true }
stats_alloc = "0.1.1"
futures = "0.3"
glob = "0.3"
tokio = { version = "1.38.0", features = ["test-util", "macros"] }

# proc macro to iterate over yml files in tests, has to be own crate.
test_each_file = { path = "test_each_file" }
Expand Down
62 changes: 32 additions & 30 deletions src/device_detector.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use anyhow::Result;

#[cfg(feature = "cache")]
use std::sync::Arc;
use get_size::GetSize;
use serde::Serialize;

use crate::client_hints::ClientHint;
Expand All @@ -8,19 +10,19 @@ use crate::parsers::device::DeviceType;
use crate::parsers::{bot, client, device, oss};

#[cfg(feature = "cache")]
use moka::sync::Cache;
use moka::future::Cache;

pub use bot::Bot;

// TODO we should Box KnownDevice as it is much larger than Bot
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Clone)]
#[derive(Debug, Clone, GetSize)]
pub enum Detection {
Known(KnownDevice),
Bot(Bot),
}

#[derive(Clone, Debug, Serialize)]
#[derive(Clone, Debug, Serialize, GetSize)]
pub struct KnownDevice {
pub client: Option<client::Client>,
pub device: Option<device::Device>,
Expand Down Expand Up @@ -540,14 +542,12 @@ impl KnownDevice {
// use std::alloc::System;

#[cfg(feature = "cache")]
type DetectionCache = Cache<String, Detection>;
type DetectionCache = Arc<Cache<String, Detection>>;

#[derive(Clone)]
pub struct DeviceDetector {
#[cfg(feature = "cache")]
caching: bool,
#[cfg(feature = "cache")]
cache: DetectionCache,
cache: Option<DetectionCache>,
}

impl DeviceDetector {
Expand All @@ -560,28 +560,26 @@ impl DeviceDetector {
#[cfg(feature = "cache")]
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
Self {
caching: false,
cache: Cache::new(0),
}
Self { cache: None }
}

#[cfg(feature = "cache")]
pub fn new_with_cache(entries: u64) -> Self {
Self {
caching: true,
cache: Cache::new(entries),
}
pub fn new_with_cache(cache: DetectionCache) -> Self {
Self { cache: Some(cache) }
}
pub fn parse(&self, ua: &str, headers: Option<Vec<(String, String)>>) -> Result<Detection> {
pub async fn parse(
&self,
ua: &str,
headers: Option<Vec<(String, String)>>,
) -> Result<Detection> {
let client_hints = match headers {
Some(headers) => Some(ClientHint::from_headers(headers)?),
None => None,
};
self.parse_client_hints(ua, client_hints)
self.parse_client_hints(ua, client_hints).await
}

pub fn parse_client_hints(
pub async fn parse_client_hints(
&self,
ua: &str,
client_hints: Option<ClientHint>,
Expand All @@ -604,19 +602,23 @@ impl DeviceDetector {

#[cfg(feature = "cache")]
{
if !self.caching {
return Ok(parse()?);
}

if let Some(res) = self.cache.get(ua) {
return Ok(res);
};
match &self.cache {
Some(cache) => {
let result = cache.get(ua).await;

let known = parse()?;
match result {
Some(res) => return Ok(res),
None => {
let known = parse()?;

self.cache.insert(ua.to_owned(), known.clone());
cache.insert(ua.to_owned(), known.clone()).await;

Ok(known)
Ok(known)
}
}
}
None => Ok(parse()?),
}
}

#[cfg(not(feature = "cache"))]
Expand Down
4 changes: 2 additions & 2 deletions src/known_browsers.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::collections::{HashMap, HashSet};

use get_size::GetSize;
use serde::Serialize;

// These are the known browsers from matamo's device detector. While this
Expand All @@ -12,7 +12,7 @@ pub struct AvailableBrowsers {
browsers_by_name: HashMap<String, AvailableBrowser>,
}

#[derive(Clone, Debug, Serialize)]
#[derive(Clone, Debug, Serialize, GetSize)]
pub struct AvailableBrowser {
pub name: String,
pub family: Option<String>,
Expand Down
5 changes: 4 additions & 1 deletion src/known_oss.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,10 @@ fn os_families() -> HashMap<String, Vec<String>> {
),
("WebTV", vec!["WTV"]),
("Windows", vec!["WIN"]),
("Windows Mobile", vec!["WPH", "WMO", "WCE", "WRT", "WIO", "KIN"]),
(
"Windows Mobile",
vec!["WPH", "WMO", "WCE", "WRT", "WIO", "KIN"],
),
("Other Smart TV", vec!["WHS"]),
]
.into_iter()
Expand Down
6 changes: 3 additions & 3 deletions src/parsers/bot.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use anyhow::Result;

use get_size::GetSize;
use serde::Deserialize;

use once_cell::sync::Lazy;
Expand All @@ -16,15 +16,15 @@ pub fn lookup_bot(ua: &str) -> Result<Option<Bot>> {
BOT_LIST.lookup(ua)
}

#[derive(Clone, Debug, Serialize)]
#[derive(Clone, Debug, Serialize, GetSize)]
pub struct Bot {
pub name: String,
pub category: Option<String>,
pub url: Option<String>,
pub producer: Option<BotProducer>,
}

#[derive(Debug, Deserialize, Clone, Serialize)]
#[derive(Debug, Deserialize, Clone, Serialize, GetSize)]
pub struct BotProducer {
pub name: Option<String>,
pub url: Option<String>,
Expand Down
6 changes: 3 additions & 3 deletions src/parsers/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
//

use anyhow::Result;

use get_size::GetSize;
use serde::{Deserialize, Serialize};

use serde::de::Deserializer;
Expand All @@ -37,7 +37,7 @@ pub mod pim;
use crate::client_hints::ClientHint;

#[repr(C)]
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, GetSize)]
pub enum ClientType {
#[serde(rename = "browser")]
Browser,
Expand Down Expand Up @@ -66,7 +66,7 @@ impl ClientType {
}
}

#[derive(Clone, Debug, Serialize)]
#[derive(Clone, Debug, Serialize, GetSize)]
pub struct Client {
pub name: String,
pub version: Option<String>,
Expand Down
6 changes: 3 additions & 3 deletions src/parsers/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use version_compare::{self, Version};
use super::vendor_fragments;

use std::borrow::Cow;

use get_size::GetSize;
use crate::client_hints::ClientHint;
use crate::parsers::client::{Client, ClientType};
use crate::parsers::oss::OS;
Expand All @@ -29,7 +29,7 @@ pub mod portable_media_players;
pub mod shell_tvs;
pub mod televisions;

#[derive(Debug, PartialEq, Eq, Clone, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Serialize, GetSize)]
pub enum DeviceType {
#[serde(rename = "desktop")]
Desktop,
Expand Down Expand Up @@ -108,7 +108,7 @@ impl DeviceType {
}
}

#[derive(Clone, Debug, Default, Serialize)]
#[derive(Clone, Debug, Default, Serialize, GetSize)]
pub struct Device {
#[serde(rename = "type")]
pub device_type: Option<DeviceType>,
Expand Down
4 changes: 2 additions & 2 deletions src/parsers/oss.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use serde_yaml::Value;
use serde::{Deserialize, Serialize};

use std::collections::HashMap;

use get_size::GetSize;
use crate::client_hints::ClientHint;
use crate::known_oss::AvailableOSs;
use crate::parsers::utils::{
Expand Down Expand Up @@ -75,7 +75,7 @@ static LINEAGE_OS_VERSION: Lazy<HashMap<&str, &str>> = Lazy::new(|| {
.collect::<HashMap<_, _>>()
});

#[derive(Clone, Debug, Default, Serialize)]
#[derive(Clone, Debug, Default, Serialize, GetSize)]
pub struct OS {
pub name: String,
pub version: Option<String>,
Expand Down
6 changes: 3 additions & 3 deletions test_each_file/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,9 @@ fn generate_from_tree(tree: &Paths, parsed: &ForEachFile, stream: &mut TokenStre
};

stream.extend(quote! {
#[test]
fn #file_name() {
(#function)(#file_name_str, #content)
#[tokio::test]
async fn #file_name() {
(#function)(#file_name_str, #content).await
}
});
}
Expand Down
4 changes: 2 additions & 2 deletions tests/php_tests/bots.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ use serde_yaml::Value;

use crate::utils;

pub(crate) fn basic(idx: usize, value: &Value) -> Result<()> {
pub(crate) async fn basic(idx: usize, value: &Value) -> Result<()> {
let ua = value["user_agent"].as_str().expect("user_agent");
let test_bot = value["bot"].as_mapping().expect("bot");
let dd = &utils::DD;
let dd_res = dd.parse(ua, None)?;
let dd_res = dd.parse(ua, None).await?;

assert!(
dd_res.is_bot(),
Expand Down
14 changes: 7 additions & 7 deletions tests/php_tests/fixtures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,31 +37,31 @@ test_each_file! { in "./tests/data/fixtures/" => base_fixture_tests }
// Ok(())
//}
//
fn base_fixture_tests(file_path: &str, contents: &str) {
async fn base_fixture_tests(file_path: &str, contents: &str) {
let mut cases: Value = serde_yaml::from_str(contents).expect("valid test yaml");

let cases = cases.as_sequence_mut().expect("sequence");

for (i, case) in cases.into_iter().enumerate() {
basic(file_path, i + 1, case).expect("basic test");
basic(file_path, i + 1, case).await.expect("basic test");
}
}

fn basic(test_file: &str, idx: usize, value: &Value) -> Result<()> {
async fn basic(test_file: &str, idx: usize, value: &Value) -> Result<()> {
if value
.as_mapping()
.map(|m| m.contains_key("bot"))
.unwrap_or(false)
{
crate::bots::basic(idx, value)?;
crate::bots::basic(idx, value).await?;
} else {
basic_known(test_file, idx, value)?;
basic_known(test_file, idx, value).await?;
}

Ok(())
}

fn basic_known(file_path: &str, idx: usize, value: &Value) -> Result<()> {
async fn basic_known(file_path: &str, idx: usize, value: &Value) -> Result<()> {
let dd = &utils::DD;

let ua = value["user_agent"]
Expand All @@ -73,7 +73,7 @@ fn basic_known(file_path: &str, idx: usize, value: &Value) -> Result<()> {
.and_then(|headers| headers.as_mapping())
.and_then(|headers| utils::client_hint_mock(headers).ok());

let dd_res = dd.parse_client_hints(ua, client_hints)?;
let dd_res = dd.parse_client_hints(ua, client_hints).await?;
assert!(!dd_res.is_bot(), "should not be a bot");

basic_os(file_path, idx, ua, value, &dd_res)?;
Expand Down
10 changes: 5 additions & 5 deletions tests/php_tests/parser/client/browsers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ use std::io::BufReader;

use rust_device_detector::client_hints::ClientHint;

#[test]
fn test_parser_browsers() -> Result<()> {
#[tokio::test]
async fn test_parser_browsers() -> Result<()> {
let files: Vec<BufReader<File>> = vec![BufReader::new(
File::open("tests/data/fixtures/parser/client/browser.yml").expect("file"),
)];
Expand All @@ -20,14 +20,14 @@ fn test_parser_browsers() -> Result<()> {
let cases = cases.as_sequence_mut().expect("sequence");

for (i, case) in cases.into_iter().enumerate() {
basic(i + 1, case).expect("basic test");
basic(i + 1, case).await.expect("basic test");
}
}

Ok(())
}

fn basic(idx: usize, value: &mut Value) -> Result<()> {
async fn basic(idx: usize, value: &mut Value) -> Result<()> {
let ua = value["user_agent"].as_str().expect("user_agent");
let test_client = value["client"].as_mapping().expect("client");
let dd = &utils::DD;
Expand All @@ -37,7 +37,7 @@ fn basic(idx: usize, value: &mut Value) -> Result<()> {
.and_then(|headers| headers.as_mapping())
.and_then(|headers| utils::client_hint_mock(headers).ok());

let dd_res = dd.parse_client_hints(ua, client_hints)?;
let dd_res = dd.parse_client_hints(ua, client_hints).await?;

assert!(!dd_res.is_bot());

Expand Down
Loading
Loading