mirror of
https://github.com/2e3s/awatcher.git
synced 2025-06-05 19:15:33 +00:00
Implement window data filtration
This commit is contained in:
parent
f63033b6f3
commit
69130e47a8
13
Cargo.lock
generated
13
Cargo.lock
generated
@ -4,9 +4,9 @@ version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.20"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
|
||||
checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@ -223,6 +223,7 @@ dependencies = [
|
||||
"fern",
|
||||
"gethostname 0.4.1",
|
||||
"log",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_default",
|
||||
"serde_json",
|
||||
@ -1405,9 +1406,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.7.3"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d"
|
||||
checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@ -1416,9 +1417,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.29"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
||||
checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c"
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
|
@ -20,3 +20,4 @@ toml = "0.7.3"
|
||||
dirs = "5.0.0"
|
||||
serde = { version = "1.0.160", features = ["derive"] }
|
||||
serde_default = "0.1.0"
|
||||
regex = "1.8.1"
|
||||
|
@ -1,11 +1,14 @@
|
||||
mod defaults;
|
||||
mod file_config;
|
||||
mod filters;
|
||||
|
||||
use crate::BoxedError;
|
||||
use clap::{arg, value_parser, Command};
|
||||
use file_config::FileConfig;
|
||||
use std::{path::PathBuf, time::Duration};
|
||||
|
||||
use self::filters::{Filter, Replacement};
|
||||
|
||||
pub struct Config {
|
||||
pub port: u32,
|
||||
pub host: String,
|
||||
@ -14,6 +17,7 @@ pub struct Config {
|
||||
pub poll_time_window: Duration,
|
||||
pub idle_bucket_name: String,
|
||||
pub active_window_bucket_name: String,
|
||||
filters: Vec<Filter>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
@ -55,6 +59,17 @@ impl Config {
|
||||
poll_time_window: config.client.get_poll_time_window(),
|
||||
idle_bucket_name,
|
||||
active_window_bucket_name,
|
||||
filters: config.client.filters,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn window_data_replacement(&self, app_id: &str, title: &str) -> Replacement {
|
||||
for filter in &self.filters {
|
||||
if let Some(replacement) = filter.replacement(app_id, title) {
|
||||
return replacement;
|
||||
}
|
||||
}
|
||||
|
||||
Replacement::default()
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,8 @@ use std::{
|
||||
|
||||
use crate::{config::defaults, BoxedError};
|
||||
|
||||
use super::filters::Filter;
|
||||
|
||||
#[derive(Deserialize, DefaultFromSerde)]
|
||||
pub struct ServerConfig {
|
||||
#[serde(default = "defaults::port")]
|
||||
@ -18,6 +20,7 @@ pub struct ServerConfig {
|
||||
}
|
||||
|
||||
#[derive(Deserialize, DefaultFromSerde)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct ClientConfig {
|
||||
#[serde(default = "defaults::idle_timeout_seconds")]
|
||||
idle_timeout_seconds: u32,
|
||||
@ -25,6 +28,8 @@ pub struct ClientConfig {
|
||||
poll_time_idle_seconds: u32,
|
||||
#[serde(default = "defaults::poll_time_window_seconds")]
|
||||
poll_time_window_seconds: u32,
|
||||
#[serde(default)]
|
||||
pub filters: Vec<Filter>,
|
||||
}
|
||||
|
||||
impl ClientConfig {
|
||||
@ -83,6 +88,19 @@ impl FileConfig {
|
||||
# idle-timeout-seconds={}
|
||||
# poll-time-idle-seconds={}
|
||||
# poll-time-window-seconds={}
|
||||
|
||||
# Add as many filters as needed. The first matching filter stops the replacement.
|
||||
# There should be at least 1 match field, and at least 1 replace field.
|
||||
# Matches are case sensitive regular expressions between implici ^ and $, e.g.
|
||||
# - "." matches 1 any character
|
||||
# - ".*" matches any number of any characters
|
||||
# - ".+" matches 1 or more any characters.
|
||||
# - "word" is an exact match.
|
||||
# [[awatcher.filters]]
|
||||
# match-app-id = "navigator"
|
||||
# match-title = ".*Firefox.*"
|
||||
# replace-app-id = "firefox"
|
||||
# replace-title = "Unknown"
|
||||
"#,
|
||||
defaults::port(),
|
||||
defaults::host(),
|
||||
|
79
src/config/filters.rs
Normal file
79
src/config/filters.rs
Normal file
@ -0,0 +1,79 @@
|
||||
use regex::Regex;
|
||||
use serde::de::Error;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct Filter {
|
||||
#[serde(default)]
|
||||
#[serde(deserialize_with = "string_to_regex")]
|
||||
match_app_id: Option<Regex>,
|
||||
#[serde(default)]
|
||||
#[serde(deserialize_with = "string_to_regex")]
|
||||
match_title: Option<Regex>,
|
||||
replace_app_id: Option<String>,
|
||||
replace_title: Option<String>,
|
||||
}
|
||||
|
||||
fn string_to_regex<'de, D>(d: D) -> Result<Option<Regex>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s = <Option<String>>::deserialize(d)?;
|
||||
|
||||
if let Some(s) = s {
|
||||
match format!("^{s}$").parse() {
|
||||
Ok(regex) => Ok(Some(regex)),
|
||||
Err(err) => Err(D::Error::custom(err)),
|
||||
}
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Replacement {
|
||||
pub replace_app_id: Option<String>,
|
||||
pub replace_title: Option<String>,
|
||||
}
|
||||
|
||||
impl Filter {
|
||||
fn is_valid(&self) -> bool {
|
||||
(self.match_app_id.is_some() || self.match_title.is_some())
|
||||
&& (self.replace_app_id.is_some() || self.replace_title.is_some())
|
||||
}
|
||||
|
||||
fn is_match(&self, app_id: &str, title: &str) -> bool {
|
||||
if let Some(match_app_id) = &self.match_app_id {
|
||||
if !match_app_id.is_match(app_id) {
|
||||
return false;
|
||||
};
|
||||
};
|
||||
if let Some(match_title) = &self.match_title {
|
||||
if !match_title.is_match(title) {
|
||||
return false;
|
||||
};
|
||||
};
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
pub fn replacement(&self, app_id: &str, title: &str) -> Option<Replacement> {
|
||||
if !self.is_valid() {
|
||||
return None;
|
||||
}
|
||||
|
||||
if self.is_match(app_id, title) {
|
||||
let mut replacement = Replacement::default();
|
||||
if let Some(new_app_id) = &self.replace_app_id {
|
||||
replacement.replace_app_id = Some(new_app_id.to_string());
|
||||
}
|
||||
if let Some(new_title) = &self.replace_title {
|
||||
replacement.replace_title = Some(new_title.to_string());
|
||||
}
|
||||
Some(replacement)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
@ -49,8 +49,23 @@ impl ReportClient {
|
||||
|
||||
pub fn send_active_window(&self, app_id: &str, title: &str) -> Result<(), BoxedError> {
|
||||
let mut data = Map::new();
|
||||
data.insert("app".to_string(), Value::String(app_id.to_string()));
|
||||
data.insert("title".to_string(), Value::String(title.to_string()));
|
||||
let mut data_insert = |k: &str, v: String| data.insert(k.to_string(), Value::String(v));
|
||||
|
||||
let replacement = self.config.window_data_replacement(app_id, title);
|
||||
let inserted_app_id = if let Some(new_app_id) = replacement.replace_app_id {
|
||||
trace!("Replacing app_id by {new_app_id}");
|
||||
new_app_id
|
||||
} else {
|
||||
app_id.to_string()
|
||||
};
|
||||
let inserted_title = if let Some(new_title) = replacement.replace_title {
|
||||
trace!("Replacing title of {inserted_app_id} by {new_title}");
|
||||
new_title
|
||||
} else {
|
||||
title.to_string()
|
||||
};
|
||||
data_insert("app", inserted_app_id);
|
||||
data_insert("title", inserted_title);
|
||||
let event = AwEvent {
|
||||
id: None,
|
||||
timestamp: Utc::now(),
|
||||
|
Loading…
x
Reference in New Issue
Block a user