Refactor config handling
This commit is contained in:
parent
bd4850969b
commit
66228bbefe
|
@ -0,0 +1,42 @@
|
||||||
|
use miette::{IntoDiagnostic, Result};
|
||||||
|
use std::{fs, ops::Deref};
|
||||||
|
|
||||||
|
#[derive(knuffel::Decode, Debug)]
|
||||||
|
pub(crate) struct Category {
|
||||||
|
#[knuffel(argument)]
|
||||||
|
pub name: String,
|
||||||
|
#[knuffel(property, default)]
|
||||||
|
disabled: bool,
|
||||||
|
#[knuffel(child, unwrap(arguments))]
|
||||||
|
pub params: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct Config(Vec<Category>);
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
/// Read filter file
|
||||||
|
pub fn from_file(path: &str) -> Result<Self> {
|
||||||
|
let filters = fs::read_to_string(path)
|
||||||
|
.into_diagnostic()
|
||||||
|
.map_err(|err| err.context(format!("Could not read file `{path}`")))?;
|
||||||
|
|
||||||
|
let filters = knuffel::parse::<Vec<Category>>("config.kdl", &filters)?
|
||||||
|
.into_iter()
|
||||||
|
.filter(|v| !v.disabled)
|
||||||
|
.collect::<Vec<Category>>();
|
||||||
|
|
||||||
|
Ok(Config(filters))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flat(&self) -> Vec<String> {
|
||||||
|
self.iter().flat_map(|v| v.params.clone()).collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for Config {
|
||||||
|
type Target = Vec<Category>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
46
src/main.rs
46
src/main.rs
|
@ -1,47 +1,29 @@
|
||||||
use arboard::Clipboard;
|
use arboard::Clipboard;
|
||||||
use memoize::memoize;
|
use memoize::memoize;
|
||||||
use miette::{miette, IntoDiagnostic, Result};
|
use miette::{miette, Result};
|
||||||
use std::{env, fs, time::Duration};
|
use std::{env, time::Duration};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use wildmatch::WildMatch;
|
use wildmatch::WildMatch;
|
||||||
|
|
||||||
#[derive(knuffel::Decode, Debug)]
|
mod config;
|
||||||
struct Category {
|
use config::Config;
|
||||||
#[knuffel(argument)]
|
|
||||||
name: String,
|
|
||||||
#[knuffel(property, default)]
|
|
||||||
disabled: bool,
|
|
||||||
#[knuffel(child, unwrap(arguments))]
|
|
||||||
params: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// How often should clipboard be checked for changes (0 will result in high CPU usage)
|
/// How often should clipboard be checked for changes (0 will result in high CPU usage)
|
||||||
const ITERATION_DELAY: Duration = Duration::from_millis(250);
|
const ITERATION_DELAY: Duration = Duration::from_millis(250);
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
// Get filter file path
|
// Get filter file path
|
||||||
let filters = env::args()
|
let path = env::args()
|
||||||
.nth(1)
|
.nth(1)
|
||||||
.ok_or_else(|| miette!("Please provide a path to a KDL file with parameter filters"))?;
|
.ok_or_else(|| miette!("Please provide a path to a KDL file with parameter filters"))?;
|
||||||
|
|
||||||
// Read filter file
|
let filters = Config::from_file(&path)?;
|
||||||
let filters = fs::read_to_string(&filters)
|
|
||||||
.into_diagnostic()
|
|
||||||
.map_err(|err| err.context(format!("Could not read file `{filters}`")))?;
|
|
||||||
|
|
||||||
let filters = knuffel::parse::<Vec<Category>>("config.kdl", &filters)?
|
|
||||||
.into_iter()
|
|
||||||
.filter(|v| !v.disabled)
|
|
||||||
.collect::<Vec<Category>>();
|
|
||||||
|
|
||||||
println!("Loaded with categories:");
|
println!("Loaded with categories:");
|
||||||
for filter in &filters {
|
for filter in &*filters {
|
||||||
println!("\t• {}", filter.name);
|
println!("\t• {}", filter.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flatten filters into patterns
|
|
||||||
let patterns: Vec<String> = filters.iter().flat_map(|v| v.params.clone()).collect();
|
|
||||||
|
|
||||||
// Initialize clipboard context
|
// Initialize clipboard context
|
||||||
let mut clipboard = Clipboard::new()
|
let mut clipboard = Clipboard::new()
|
||||||
.map_err(|e| miette!(format!("Could not initialize clipboard context: {e}")))?;
|
.map_err(|e| miette!(format!("Could not initialize clipboard context: {e}")))?;
|
||||||
|
@ -57,7 +39,7 @@ fn main() -> Result<()> {
|
||||||
// Clipboard changed
|
// Clipboard changed
|
||||||
if contents != last_contents {
|
if contents != last_contents {
|
||||||
last_contents = contents.clone();
|
last_contents = contents.clone();
|
||||||
if let Ok(url) = clean_url(contents, patterns.clone()) {
|
if let Ok(url) = clean_url(contents, filters.flat()) {
|
||||||
// Update clipboard
|
// Update clipboard
|
||||||
clipboard
|
clipboard
|
||||||
.set_text(url.clone())
|
.set_text(url.clone())
|
||||||
|
@ -71,7 +53,7 @@ fn main() -> Result<()> {
|
||||||
|
|
||||||
#[memoize(Capacity: 1024)]
|
#[memoize(Capacity: 1024)]
|
||||||
fn clean_url(text: String, patterns: Vec<String>) -> Result<String, String> {
|
fn clean_url(text: String, patterns: Vec<String>) -> Result<String, String> {
|
||||||
if let Ok(mut url) = Url::parse(&text) {
|
let mut url = Url::parse(&text).map_err(|e| format!("Contents are not a valid URL: {e}"))?;
|
||||||
let url_inner = url.clone();
|
let url_inner = url.clone();
|
||||||
|
|
||||||
// Skip URLs without a host
|
// Skip URLs without a host
|
||||||
|
@ -81,8 +63,7 @@ fn clean_url(text: String, patterns: Vec<String>) -> Result<String, String> {
|
||||||
|
|
||||||
for pattern in &patterns {
|
for pattern in &patterns {
|
||||||
let url_inner = url.clone();
|
let url_inner = url.clone();
|
||||||
match pattern.split_once('@') {
|
if let Some((param, domain)) = pattern.split_once('@') {
|
||||||
Some((param, domain)) => {
|
|
||||||
if WildMatch::new(domain).matches(host) {
|
if WildMatch::new(domain).matches(host) {
|
||||||
// Filter parameters to exclude blocked entries
|
// Filter parameters to exclude blocked entries
|
||||||
let query = url_inner
|
let query = url_inner
|
||||||
|
@ -91,8 +72,7 @@ fn clean_url(text: String, patterns: Vec<String>) -> Result<String, String> {
|
||||||
// Replace parameters in URL
|
// Replace parameters in URL
|
||||||
url.query_pairs_mut().clear().extend_pairs(query);
|
url.query_pairs_mut().clear().extend_pairs(query);
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
None => {
|
|
||||||
// Filter parameters to exclude blocked entries
|
// Filter parameters to exclude blocked entries
|
||||||
let query = url_inner
|
let query = url_inner
|
||||||
.query_pairs()
|
.query_pairs()
|
||||||
|
@ -101,13 +81,9 @@ fn clean_url(text: String, patterns: Vec<String>) -> Result<String, String> {
|
||||||
url.query_pairs_mut().clear().extend_pairs(query);
|
url.query_pairs_mut().clear().extend_pairs(query);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Handle dangling ?s when no query pairs are appended
|
// Handle dangling ?s when no query pairs are appended
|
||||||
let url = url.as_str().trim_end_matches('?').to_owned();
|
let url = url.as_str().trim_end_matches('?').to_owned();
|
||||||
|
|
||||||
Ok(url)
|
Ok(url)
|
||||||
} else {
|
|
||||||
Err(String::from("Contents are not a valid URL"))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue