diff --git a/src/config.rs b/src/config.rs index 4f9a8a0..b4575cc 100644 --- a/src/config.rs +++ b/src/config.rs @@ -5,102 +5,102 @@ use serde_derive::Deserialize; #[derive(Debug, Deserialize)] pub struct Replacements<'config> { - #[serde(rename = "substitutor", borrow, default)] - pub substitutors: Vec>, + #[serde(rename = "substitutor", borrow, default)] + pub substitutors: Vec>, } #[derive(Debug, Deserialize)] pub struct Substitutor<'config> { - #[serde(default)] - pub name: &'config str, - #[serde(borrow, alias = "matcher")] - pub matcher: MatcherType<'config>, - #[serde(borrow)] - pub action: Action<'config>, + #[serde(default)] + pub name: &'config str, + #[serde(borrow, alias = "matcher")] + pub matcher: MatcherType<'config>, + #[serde(borrow)] + pub action: Action<'config>, } #[derive(Debug, Deserialize)] #[serde(untagged)] pub enum MatcherType<'config> { - #[serde(borrow)] - Single(Matcher<'config>), - #[serde(borrow)] - Multiple(Vec>), + #[serde(borrow)] + Single(Matcher<'config>), + #[serde(borrow)] + Multiple(Vec>), } #[derive(Debug, Deserialize)] pub enum Matcher<'config> { - #[serde(rename = "starts_with")] - StartsWith { prefix: &'config str }, - #[serde(rename = "ends_with")] - EndsWith { suffix: &'config str }, - #[serde(rename = "contains")] - Contains { substring: &'config str }, - #[serde(rename = "regex")] - Regex { pattern: &'config str }, - #[serde(rename = "exactly")] - Exactly { content: &'config str }, + #[serde(rename = "starts_with")] + StartsWith { prefix: &'config str }, + #[serde(rename = "ends_with")] + EndsWith { suffix: &'config str }, + #[serde(rename = "contains")] + Contains { substring: &'config str }, + #[serde(rename = "regex")] + Regex { pattern: &'config str }, + #[serde(rename = "exactly")] + Exactly { content: &'config str }, } #[derive(Debug, Deserialize)] pub enum Action<'config> { - #[serde(rename = "set")] - Set { content: &'config str }, - #[serde(rename = "replace")] - Replace { - from: &'config str, - to: &'config str, - }, - #[serde(rename = "prefix")] - Prefix { prefix: &'config str }, - #[serde(rename = "suffix")] - Suffix { suffix: &'config str }, + #[serde(rename = "set")] + Set { content: &'config str }, + #[serde(rename = "replace")] + Replace { + from: &'config str, + to: &'config str, + }, + #[serde(rename = "prefix")] + Prefix { prefix: &'config str }, + #[serde(rename = "suffix")] + Suffix { suffix: &'config str }, } pub trait Match { - fn check_match(&self, string: &str) -> bool; + fn check_match(&self, string: &str) -> bool; } pub trait Act { - fn apply_action(&self, input: &str) -> String; + fn apply_action(&self, input: &str) -> String; } impl Match for Matcher<'_> { - fn check_match(&self, string: &str) -> bool { - match self { - Matcher::StartsWith { prefix } => string.starts_with(prefix), - Matcher::EndsWith { suffix } => string.ends_with(suffix), - Matcher::Contains { substring } => string.contains(substring), - Matcher::Regex { pattern } => { - if let Ok(regex) = Regex::from_str(pattern) { - regex.is_match(string) - } else { - false - } - } - Matcher::Exactly { content } => &string == content, + fn check_match(&self, string: &str) -> bool { + match self { + Matcher::StartsWith { prefix } => string.starts_with(prefix), + Matcher::EndsWith { suffix } => string.ends_with(suffix), + Matcher::Contains { substring } => string.contains(substring), + Matcher::Regex { pattern } => { + if let Ok(regex) = Regex::from_str(pattern) { + regex.is_match(string) + } else { + false } + } + Matcher::Exactly { content } => &string == content, } + } } impl Match for MatcherType<'_> { - fn check_match(&self, string: &str) -> bool { - match self { - MatcherType::Single(matcher) => matcher.check_match(string), - MatcherType::Multiple(matchers) => { - matchers.iter().all(|matcher| matcher.check_match(string)) - } - } + fn check_match(&self, string: &str) -> bool { + match self { + MatcherType::Single(matcher) => matcher.check_match(string), + MatcherType::Multiple(matchers) => { + matchers.iter().all(|matcher| matcher.check_match(string)) + } } + } } impl Act for Action<'_> { - fn apply_action(&self, input: &str) -> String { - return match self { - Action::Replace { from, to } => input.replace(from, to), - Action::Prefix { prefix } => format!("{prefix}{input}"), - Action::Suffix { suffix } => format!("{input}{suffix}"), - Action::Set { content } => content.to_owned().to_owned(), - }; - } + fn apply_action(&self, input: &str) -> String { + return match self { + Action::Replace { from, to } => input.replace(from, to), + Action::Prefix { prefix } => format!("{prefix}{input}"), + Action::Suffix { suffix } => format!("{input}{suffix}"), + Action::Set { content } => content.to_owned().to_owned(), + }; + } } diff --git a/src/main.rs b/src/main.rs index d56a796..0414d2f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,85 +17,90 @@ use crate::config::{Act, Match, Replacements}; const VERSION_ARGS: [&str; 3] = ["version", "-v", "--version"]; fn main() -> Result<()> { - if check_for_version_arg() { - return Ok(()); - } - configure_tracing(); - let config_path = get_config_path()?; - let config_str = std::fs::read_to_string(config_path.as_path()).unwrap_or_default(); - let config: Replacements<'_> = toml::from_str(&config_str)?; - loop_clipboard(config); - Ok(()) + if check_for_version_arg() { + return Ok(()); + } + configure_tracing(); + let config_path = get_config_path()?; + let config_str = + std::fs::read_to_string(config_path.as_path()).unwrap_or_default(); + let config: Replacements<'_> = toml::from_str(&config_str)?; + loop_clipboard(config); + Ok(()) } fn check_for_version_arg() -> bool { - let args: Vec = std::env::args().collect(); - for arg in args { - if VERSION_ARGS.contains(&arg.deref()) { - print_version(); - return true; - } + let args: Vec = std::env::args().collect(); + for arg in args { + if VERSION_ARGS.contains(&arg.deref()) { + print_version(); + return true; } - false + } + false } fn print_version() { - println!( - "{}", - concat!(env!("CARGO_PKG_NAME"), " ", env!("CARGO_PKG_VERSION")) - ); + println!( + "{}", + concat!(env!("CARGO_PKG_NAME"), " ", env!("CARGO_PKG_VERSION")) + ); } fn configure_tracing() { - let subscriber = FmtSubscriber::builder() - .with_max_level(Level::TRACE) - .finish(); + let subscriber = FmtSubscriber::builder() + .with_max_level(Level::TRACE) + .finish(); - tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed"); + tracing::subscriber::set_global_default(subscriber) + .expect("setting default subscriber failed"); } fn get_config_path() -> Result { - let mut config_path = config_dir().ok_or_else(|| anyhow!("Failed to get config dir"))?; - config_path.push("substitutor"); - config_path.push("config"); - config_path.set_extension("toml"); - Ok(config_path) + let mut config_path = + config_dir().ok_or_else(|| anyhow!("Failed to get config dir"))?; + config_path.push("substitutor"); + config_path.push("config"); + config_path.set_extension("toml"); + Ok(config_path) } fn loop_clipboard(config: Replacements) { - let mut clipboard: ClipboardContext = - ClipboardProvider::new().expect("Failed to get clipboard"); - let mut clipboard_contents = get_clipboard_contents(&mut clipboard); - while let Ok(contents) = clipboard_contents.as_deref() { - if let Some(subst) = config - .substitutors - .iter() - .find(|subst| subst.matcher.check_match(contents)) - { - if subst.name.is_empty().not() { - debug!("{}: matched on {}...", &subst.name, truncate(contents, 40)); - } - let result = subst.action.apply_action(contents); - if let Err(e) = clipboard.set_contents(result.to_owned()) { - error!("{e}"); - } - }; - while let Ok(new_contents) = get_clipboard_contents(&mut clipboard) { - if new_contents != contents { - clipboard_contents = Ok(new_contents); - break; - }; - } + let mut clipboard: ClipboardContext = + ClipboardProvider::new().expect("Failed to get clipboard"); + let mut clipboard_contents = get_clipboard_contents(&mut clipboard); + while let Ok(contents) = clipboard_contents.as_deref() { + if let Some(subst) = config + .substitutors + .iter() + .find(|subst| subst.matcher.check_match(contents)) + { + if subst.name.is_empty().not() { + debug!("{}: matched on {}...", &subst.name, truncate(contents, 40)); + } + let result = subst.action.apply_action(contents); + if let Err(e) = clipboard.set_contents(result.to_owned()) { + error!("{e}"); + } + }; + while let Ok(new_contents) = get_clipboard_contents(&mut clipboard) { + if new_contents != contents { + clipboard_contents = Ok(new_contents); + break; + }; } + } } -fn get_clipboard_contents(clipboard: &mut ClipboardContext) -> Result> { - clipboard.get_contents() +fn get_clipboard_contents( + clipboard: &mut ClipboardContext, +) -> Result> { + clipboard.get_contents() } fn truncate(s: &str, max_chars: usize) -> &str { - match s.char_indices().nth(max_chars) { - None => s, - Some((idx, _)) => &s[..idx], - } + match s.char_indices().nth(max_chars) { + None => s, + Some((idx, _)) => &s[..idx], + } } diff --git a/src/test.rs b/src/test.rs index e22fc13..1c5f0ec 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,45 +1,46 @@ -use crate::config::{Act, Action, Match, Matcher, MatcherType, Replacements}; use assay::assay; +use crate::config::{Act, Action, Match, Matcher, MatcherType, Replacements}; + #[assay] fn regex_matcher() { - let matcher = Matcher::Regex { - pattern: "^https.*", - }; - assert!(matcher.check_match("https://example.com")); - assert!(!matcher.check_match("example.com")); + let matcher = Matcher::Regex { + pattern: "^https.*", + }; + assert!(matcher.check_match("https://example.com")); + assert!(!matcher.check_match("example.com")); } #[assay] fn set_action() { - let action = Action::Set { content: "doe" }; - assert_eq!("doe", action.apply_action("john")); + let action = Action::Set { content: "doe" }; + assert_eq!("doe", action.apply_action("john")); } #[assay] fn replace_action() { - let action = Action::Replace { - from: "doe", - to: "bow", - }; - assert_eq!("john bow", action.apply_action("john doe")); + let action = Action::Replace { + from: "doe", + to: "bow", + }; + assert_eq!("john bow", action.apply_action("john doe")); } #[assay] fn prefix_action() { - let action = Action::Prefix { prefix: "hello " }; - assert_eq!("hello john", action.apply_action("john")); + let action = Action::Prefix { prefix: "hello " }; + assert_eq!("hello john", action.apply_action("john")); } #[assay] fn suffix_action() { - let action = Action::Suffix { suffix: " doe" }; - assert_eq!("john doe", action.apply_action("john")); + let action = Action::Suffix { suffix: " doe" }; + assert_eq!("john doe", action.apply_action("john")); } #[assay] fn parse_with_multiple_matchers() { - let config = r#" + let config = r#" [[substitutor]] name = "Example" matcher = [ @@ -48,26 +49,26 @@ fn parse_with_multiple_matchers() { ] action = { prefix = { prefix = "/mirror" } } "#; - let config: Replacements<'_> = toml::from_str(config)?; - assert_eq!(1, config.substitutors.len()); - let subst = &config.substitutors[0]; - assert_eq!("Example", subst.name); - assert!(matches!(subst.matcher, MatcherType::Multiple(_))); - assert!(matches!(subst.action, Action::Prefix { .. })); + let config: Replacements<'_> = toml::from_str(config)?; + assert_eq!(1, config.substitutors.len()); + let subst = &config.substitutors[0]; + assert_eq!("Example", subst.name); + assert!(matches!(subst.matcher, MatcherType::Multiple(_))); + assert!(matches!(subst.action, Action::Prefix { .. })); } #[assay] fn parse_with_single_matcher() { - let config = r#" + let config = r#" [[substitutor]] name = "Example" matcher = { starts_with = { prefix = "https://example.com" } } action = { prefix = { prefix = "/mirror" } } "#; - let config: Replacements<'_> = toml::from_str(config)?; - assert_eq!(1, config.substitutors.len()); - let subst = &config.substitutors[0]; - assert_eq!("Example", subst.name); - assert!(matches!(subst.matcher, MatcherType::Single(_))); - assert!(matches!(subst.action, Action::Prefix { .. })); + let config: Replacements<'_> = toml::from_str(config)?; + assert_eq!(1, config.substitutors.len()); + let subst = &config.substitutors[0]; + assert_eq!("Example", subst.name); + assert!(matches!(subst.matcher, MatcherType::Single(_))); + assert!(matches!(subst.action, Action::Prefix { .. })); }