mirror of
https://github.com/2e3s/awatcher.git
synced 2025-06-04 02:20:15 +00:00
Allow not reporting data by filter
This commit is contained in:
parent
b4cf791a5e
commit
16942ea163
@ -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
|
||||
|
@ -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]) {
|
||||
|
@ -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,
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -147,6 +147,8 @@ impl FileConfig {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::config::FilterResult;
|
||||
|
||||
use super::*;
|
||||
use rstest::rstest;
|
||||
use std::io::Write;
|
||||
@ -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]
|
||||
|
@ -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>,
|
||||
@ -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 {
|
||||
@ -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();
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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>),
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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};
|
||||
@ -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
|
||||
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 {
|
||||
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()
|
||||
};
|
||||
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(),
|
||||
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user