Skip to content

Commit

Permalink
Allow not reporting data by filter
Browse files Browse the repository at this point in the history
  • Loading branch information
2e3s committed Oct 11, 2024
1 parent b4cf791 commit 16942ea
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 50 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ Copy the section as many times as needed for every given filter.
- `replace-title` replaces the window title with the provided value.

The first matching filter stops the replacement.
There should be at least 1 match field, and at least 1 replace field for a valid filter.
There should be at least 1 match field for a filter to be valid.
If the replacement is not specified, the data is not reported when matched.
Matches are case sensitive regular expressions between implicit ^ and $:
- `.` matches 1 any character
- `.*` matches any number of any characters
Expand Down
5 changes: 4 additions & 1 deletion src/bundle/modules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,9 +289,12 @@ mod tests {
assert_eq!(manager.path_watchers[0].name(), "aw-test");
assert!(manager.path_watchers[0].handle.is_none()); // no starting in config

assert!(manager.start_watcher(&temp_dir.path().join("aw-test")));
let watcher_path = &temp_dir.path().join("aw-test");
assert!(manager.start_watcher(watcher_path));
assert!(manager.path_watchers[0].handle.is_some());
assert_autostart_content(&manager, &["aw-test"]);

manager.stop_watcher(watcher_path);
}

fn assert_autostart_content(manager: &Manager, watchers: &[&str]) {
Expand Down
12 changes: 7 additions & 5 deletions watchers/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ pub mod defaults;
mod file_config;
mod filters;

use self::filters::{Filter, Replacement};
use self::filters::Filter;
use chrono::Duration;
pub use file_config::FileConfig;
pub use filters::FilterResult;

pub struct Config {
pub port: u16,
Expand All @@ -17,13 +18,14 @@ pub struct Config {
}

impl Config {
pub fn window_data_replacement(&self, app_id: &str, title: &str) -> Replacement {
pub fn match_window_data(&self, app_id: &str, title: &str) -> FilterResult {
for filter in &self.filters {
if let Some(replacement) = filter.replacement(app_id, title) {
return replacement;
let result = filter.apply(app_id, title);
if matches!(result, FilterResult::Match | FilterResult::Replace(_)) {
return result;
}
}

Replacement::default()
FilterResult::Skip
}
}
43 changes: 33 additions & 10 deletions watchers/src/config/file_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ impl FileConfig {

#[cfg(test)]
mod tests {
use crate::config::FilterResult;

use super::*;
use rstest::rstest;
use std::io::Write;
Expand Down Expand Up @@ -192,16 +194,37 @@ replace-title = "Title"
assert_eq!(12, config.client.poll_time_window_seconds);

assert_eq!(2, config.client.filters.len());
let replacement1 = config.client.filters[0]
.replacement("firefox", "any")
.unwrap();
assert_eq!(None, replacement1.replace_app_id);
assert_eq!(Some("Unknown".to_string()), replacement1.replace_title);
let replacement2 = config.client.filters[1]
.replacement("code", "title")
.unwrap();
assert_eq!(Some("VSCode".to_string()), replacement2.replace_app_id);
assert_eq!(Some("Title".to_string()), replacement2.replace_title);

let replacement1 = config.client.filters[0].apply("firefox", "any");
assert!(matches!(replacement1, FilterResult::Replace(ref r) if
r.replace_app_id.is_none() &&
r.replace_title == Some("Unknown".to_string())
));

let replacement2 = config.client.filters[1].apply("code", "title");
assert!(matches!(replacement2, FilterResult::Replace(ref r) if
r.replace_app_id == Some("VSCode".to_string()) &&
r.replace_title == Some("Title".to_string())
));
}

#[rstest]
fn match_filter() {
let mut file = NamedTempFile::new().unwrap();
write!(
file,
r#"
[[awatcher.filters]]
match-app-id = "firefox"
"#
)
.unwrap();

let config = FileConfig::new(Some(file.path().to_path_buf())).unwrap();

assert_eq!(1, config.client.filters.len());
let replacement1 = config.client.filters[0].apply("firefox", "any");
assert!(matches!(replacement1, FilterResult::Match));
}

#[rstest]
Expand Down
41 changes: 29 additions & 12 deletions watchers/src/config/filters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ where
}
}

#[derive(Debug, PartialEq)]
pub enum FilterResult {
Replace(Replacement),
Match,
Skip,
}

#[derive(Default, Debug, PartialEq)]
pub struct Replacement {
pub replace_app_id: Option<String>,
Expand All @@ -39,10 +46,7 @@ pub struct Replacement {

impl Filter {
fn is_valid(&self) -> bool {
let is_match_set = self.match_app_id.is_some() || self.match_title.is_some();
let is_replacement_set = self.replace_app_id.is_some() || self.replace_title.is_some();

is_match_set && is_replacement_set
self.match_app_id.is_some() || self.match_title.is_some()
}

fn is_match(&self, app_id: &str, title: &str) -> bool {
Expand Down Expand Up @@ -70,9 +74,12 @@ impl Filter {
replacement.to_owned()
}

pub fn replacement(&self, app_id: &str, title: &str) -> Option<Replacement> {
pub fn apply(&self, app_id: &str, title: &str) -> FilterResult {
if !self.is_valid() || !self.is_match(app_id, title) {
return None;
return FilterResult::Skip;
}
if self.replace_app_id.is_none() && self.replace_title.is_none() {
return FilterResult::Match;
}

let mut replacement = Replacement::default();
Expand All @@ -83,7 +90,7 @@ impl Filter {
if let Some(new_title) = &self.replace_title {
replacement.replace_title = Some(Self::replace(&self.match_title, title, new_title));
}
Some(replacement)
FilterResult::Replace(replacement)
}
}

Expand Down Expand Up @@ -141,6 +148,12 @@ mod tests {
("org.kde.dolphin", "/home/user"),
None
)]
#[case::match_only(
(Some("org\\.kde\\.(.*)"), None),
(None, None),
("org.kde.dolphin", "/home/user"),
Some((None, None))
)]
fn replacement(
#[case] matches: (Option<&str>, Option<&str>),
#[case] replaces: (Option<&str>, Option<&str>),
Expand All @@ -159,11 +172,15 @@ mod tests {
replace_title: replace_title.map(option_string),
};

let replacement = filter.replacement(app_id, title);
let expect_replacement = expect_replacement.map(|r| Replacement {
replace_app_id: r.0.map(option_string),
replace_title: r.1.map(option_string),
});
let replacement = filter.apply(app_id, title);
let expect_replacement = match expect_replacement {
None => FilterResult::Skip,
Some((None, None)) => FilterResult::Match,
Some((replace_app_id, replace_title)) => FilterResult::Replace(Replacement {
replace_app_id: replace_app_id.map(Into::into),
replace_title: replace_title.map(Into::into),
}),
};
assert_eq!(expect_replacement, replacement);
}
}
61 changes: 40 additions & 21 deletions watchers/src/report_client.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use super::config::{Config, FilterResult};
use crate::watchers::idle::Status;

use super::config::Config;
use anyhow::Context;
use aw_client_rust::{AwClient, Event as AwEvent};
use chrono::{DateTime, TimeDelta, Utc};
Expand Down Expand Up @@ -96,26 +95,19 @@ impl ReportClient {
pub async fn send_active_window(&self, app_id: &str, title: &str) -> anyhow::Result<()> {
let mut data = Map::new();

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
if let Some((inserted_app_id, inserted_title)) = self.get_filtered_data(app_id, title) {
trace!(
"Reporting app_id: {}, title: {}",
inserted_app_id,
inserted_title
);

data.insert("app".to_string(), Value::String(inserted_app_id));
data.insert("title".to_string(), Value::String(inserted_title));
} else {
title.to_string()
};
trace!(
"Reporting app_id: {}, title: {}",
inserted_app_id,
inserted_title
);
data.insert("app".to_string(), Value::String(inserted_app_id));
data.insert("title".to_string(), Value::String(inserted_title));
return Ok(());
}

let event = AwEvent {
id: None,
timestamp: Utc::now(),
Expand All @@ -141,6 +133,33 @@ impl ReportClient {
.with_context(|| "Failed to send heartbeat for active window")
}

fn get_filtered_data(&self, app_id: &str, title: &str) -> Option<(String, String)> {
let filter_result = self.config.match_window_data(app_id, title);
match filter_result {
FilterResult::Replace(replacement) => {
let app_id = if let Some(replace_app_id) = replacement.replace_app_id {
trace!("Replacing app_id by {}", replace_app_id);
replace_app_id
} else {
app_id.to_string()
};
let title = if let Some(replace_title) = replacement.replace_title {
trace!("Replacing title by {}", replace_title);
replace_title
} else {
title.to_string()
};

Some((app_id, title))
}
FilterResult::Match => {
trace!("Matched a filter, not reported");
None
}
FilterResult::Skip => Some((app_id.to_string(), title.to_string())),
}
}

async fn create_bucket(
client: &AwClient,
bucket_name: &str,
Expand Down

0 comments on commit 16942ea

Please sign in to comment.