mirror of
https://github.com/2e3s/awatcher.git
synced 2025-06-07 20:15:28 +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]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "0.7.20"
|
version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
|
checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
@ -223,6 +223,7 @@ dependencies = [
|
|||||||
"fern",
|
"fern",
|
||||||
"gethostname 0.4.1",
|
"gethostname 0.4.1",
|
||||||
"log",
|
"log",
|
||||||
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_default",
|
"serde_default",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@ -1405,9 +1406,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.7.3"
|
version = "1.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d"
|
checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
@ -1416,9 +1417,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.6.29"
|
version = "0.7.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reqwest"
|
name = "reqwest"
|
||||||
|
@ -20,3 +20,4 @@ toml = "0.7.3"
|
|||||||
dirs = "5.0.0"
|
dirs = "5.0.0"
|
||||||
serde = { version = "1.0.160", features = ["derive"] }
|
serde = { version = "1.0.160", features = ["derive"] }
|
||||||
serde_default = "0.1.0"
|
serde_default = "0.1.0"
|
||||||
|
regex = "1.8.1"
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
mod defaults;
|
mod defaults;
|
||||||
mod file_config;
|
mod file_config;
|
||||||
|
mod filters;
|
||||||
|
|
||||||
use crate::BoxedError;
|
use crate::BoxedError;
|
||||||
use clap::{arg, value_parser, Command};
|
use clap::{arg, value_parser, Command};
|
||||||
use file_config::FileConfig;
|
use file_config::FileConfig;
|
||||||
use std::{path::PathBuf, time::Duration};
|
use std::{path::PathBuf, time::Duration};
|
||||||
|
|
||||||
|
use self::filters::{Filter, Replacement};
|
||||||
|
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub port: u32,
|
pub port: u32,
|
||||||
pub host: String,
|
pub host: String,
|
||||||
@ -14,6 +17,7 @@ pub struct Config {
|
|||||||
pub poll_time_window: Duration,
|
pub poll_time_window: Duration,
|
||||||
pub idle_bucket_name: String,
|
pub idle_bucket_name: String,
|
||||||
pub active_window_bucket_name: String,
|
pub active_window_bucket_name: String,
|
||||||
|
filters: Vec<Filter>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
@ -55,6 +59,17 @@ impl Config {
|
|||||||
poll_time_window: config.client.get_poll_time_window(),
|
poll_time_window: config.client.get_poll_time_window(),
|
||||||
idle_bucket_name,
|
idle_bucket_name,
|
||||||
active_window_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 crate::{config::defaults, BoxedError};
|
||||||
|
|
||||||
|
use super::filters::Filter;
|
||||||
|
|
||||||
#[derive(Deserialize, DefaultFromSerde)]
|
#[derive(Deserialize, DefaultFromSerde)]
|
||||||
pub struct ServerConfig {
|
pub struct ServerConfig {
|
||||||
#[serde(default = "defaults::port")]
|
#[serde(default = "defaults::port")]
|
||||||
@ -18,6 +20,7 @@ pub struct ServerConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, DefaultFromSerde)]
|
#[derive(Deserialize, DefaultFromSerde)]
|
||||||
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct ClientConfig {
|
pub struct ClientConfig {
|
||||||
#[serde(default = "defaults::idle_timeout_seconds")]
|
#[serde(default = "defaults::idle_timeout_seconds")]
|
||||||
idle_timeout_seconds: u32,
|
idle_timeout_seconds: u32,
|
||||||
@ -25,6 +28,8 @@ pub struct ClientConfig {
|
|||||||
poll_time_idle_seconds: u32,
|
poll_time_idle_seconds: u32,
|
||||||
#[serde(default = "defaults::poll_time_window_seconds")]
|
#[serde(default = "defaults::poll_time_window_seconds")]
|
||||||
poll_time_window_seconds: u32,
|
poll_time_window_seconds: u32,
|
||||||
|
#[serde(default)]
|
||||||
|
pub filters: Vec<Filter>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClientConfig {
|
impl ClientConfig {
|
||||||
@ -83,6 +88,19 @@ impl FileConfig {
|
|||||||
# idle-timeout-seconds={}
|
# idle-timeout-seconds={}
|
||||||
# poll-time-idle-seconds={}
|
# poll-time-idle-seconds={}
|
||||||
# poll-time-window-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::port(),
|
||||||
defaults::host(),
|
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> {
|
pub fn send_active_window(&self, app_id: &str, title: &str) -> Result<(), BoxedError> {
|
||||||
let mut data = Map::new();
|
let mut data = Map::new();
|
||||||
data.insert("app".to_string(), Value::String(app_id.to_string()));
|
let mut data_insert = |k: &str, v: String| data.insert(k.to_string(), Value::String(v));
|
||||||
data.insert("title".to_string(), Value::String(title.to_string()));
|
|
||||||
|
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 {
|
let event = AwEvent {
|
||||||
id: None,
|
id: None,
|
||||||
timestamp: Utc::now(),
|
timestamp: Utc::now(),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user