From ee14666333f7af56eedfcd00132442422077d3e6 Mon Sep 17 00:00:00 2001 From: graelo Date: Sun, 21 Mar 2021 07:12:14 +0100 Subject: [PATCH] refactor: CliOpt -> config/core.rs --- src/bin/copyrat.rs | 2 +- src/bin/tmux_copyrat.rs | 2 +- src/config/core.rs | 158 +++++++++++++++++++++++++++++++++++++ src/config/mod.rs | 3 + src/lib.rs | 167 ++-------------------------------------- 5 files changed, 169 insertions(+), 163 deletions(-) create mode 100644 src/config/core.rs create mode 100644 src/config/mod.rs diff --git a/src/bin/copyrat.rs b/src/bin/copyrat.rs index 4b0ab6f..592f19b 100644 --- a/src/bin/copyrat.rs +++ b/src/bin/copyrat.rs @@ -3,7 +3,7 @@ use std::fs::OpenOptions; use std::io::prelude::*; use std::io::{self, Read}; -use copyrat::{run, ui::Selection, CliOpt}; +use copyrat::{config::CliOpt, run, ui::Selection}; fn main() { let opt = CliOpt::parse(); diff --git a/src/bin/tmux_copyrat.rs b/src/bin/tmux_copyrat.rs index 8cb4299..b8faddb 100644 --- a/src/bin/tmux_copyrat.rs +++ b/src/bin/tmux_copyrat.rs @@ -4,9 +4,9 @@ use std::str::FromStr; use copyrat::{ comm::{tmux, OutputDestination}, + config::CliOpt, error, ui::Selection, - CliOpt, }; /// Main configuration, parsed from command line. diff --git a/src/config/core.rs b/src/config/core.rs new file mode 100644 index 0000000..9022f56 --- /dev/null +++ b/src/config/core.rs @@ -0,0 +1,158 @@ +use clap::Clap; +use std::collections::HashMap; +use std::path; +use std::str::FromStr; + +use crate::{ + error, + textbuf::{alphabet, regexes}, + ui, +}; + +/// Main configuration, parsed from command line. +#[derive(Clap, Debug)] +#[clap(author, about, version)] +pub struct CliOpt { + /// Alphabet to draw hints from. + /// + /// Possible values are "{A}", "{A}-homerow", "{A}-left-hand", + /// "{A}-right-hand", where "{A}" is one of "qwerty", "azerty", "qwertz" + /// "dvorak", "colemak". + /// + /// # Examples + /// + /// "qwerty", "dvorak-homerow", "azerty-right-hand". + #[clap(short = 'k', long, default_value = "dvorak", + parse(try_from_str = alphabet::parse_alphabet))] + pub alphabet: alphabet::Alphabet, + + /// Use all available regex patterns. + #[clap(short = 'A', long = "--all-patterns")] + pub use_all_patterns: bool, + + /// Pattern names to use ("email", ... see doc). + #[clap(short = 'x', long = "--pattern-name", parse(try_from_str = regexes::parse_pattern_name))] + pub named_patterns: Vec, + + /// Additional regex patterns ("foo*bar", etc). + #[clap(short = 'X', long = "--custom-pattern")] + pub custom_patterns: Vec, + + /// Assign hints starting from the bottom of the screen. + #[clap(short, long)] + pub reverse: bool, + + /// Keep the same hint for identical matches. + #[clap(short, long)] + pub unique_hint: bool, + + #[clap(flatten)] + pub colors: ui::colors::UiColors, + + /// Align hint with its match. + #[clap(long, arg_enum, default_value = "leading")] + pub hint_alignment: ui::HintAlignment, + + /// Move focus back to first/last match. + #[clap(long)] + pub focus_wrap_around: bool, + + /// Optional hint styling. + /// + /// Underline or surround the hint for increased visibility. + /// If not provided, only the hint colors will be used. + #[clap(short = 's', long, arg_enum)] + pub hint_style: Option, + + /// Chars surrounding each hint, used with `Surround` style. + #[clap(long, default_value = "{}", + parse(try_from_str = parse_chars))] + pub hint_surroundings: (char, char), + + /// Optional target path where to store the selected matches. + #[clap(short = 'o', long = "output", parse(from_os_str))] + pub target_path: Option, + + /// Describes if the uppercased marker should be added to the output, + /// indicating if hint key was uppercased. This is only used by + /// tmux-copyrat, so it is hidden (skipped) from the CLI. + #[clap(skip)] + pub uppercased_marker: bool, +} + +/// Type introduced due to parsing limitation, +/// as we cannot directly parse into ui::HintStyle. +#[derive(Debug, Clap)] +pub enum HintStyleCli { + Bold, + Italic, + Underline, + Surround, +} + +impl FromStr for HintStyleCli { + type Err = error::ParseError; + + fn from_str(s: &str) -> Result { + match s { + "leading" => Ok(HintStyleCli::Underline), + "trailing" => Ok(HintStyleCli::Surround), + _ => Err(error::ParseError::ExpectedString(String::from( + "underline or surround", + ))), + } + } +} + +/// Try to parse a `&str` into a tuple of `char`s. +fn parse_chars(src: &str) -> Result<(char, char), error::ParseError> { + if src.len() != 2 { + return Err(error::ParseError::ExpectedSurroundingPair); + } + + let chars: Vec = src.chars().collect(); + Ok((chars[0], chars[1])) +} + +impl CliOpt { + /// Try parsing provided options, and update self with the valid values. + pub fn merge_map( + &mut self, + options: &HashMap, + ) -> Result<(), error::ParseError> { + for (name, value) in options { + match name.as_ref() { + "@copyrat-alphabet" => { + self.alphabet = alphabet::parse_alphabet(value)?; + } + "@copyrat-pattern-name" => { + self.named_patterns = vec![regexes::parse_pattern_name(value)?] + } + "@copyrat-custom-pattern" => self.custom_patterns = vec![String::from(value)], + "@copyrat-reverse" => { + self.reverse = value.parse::()?; + } + "@copyrat-unique-hint" => { + self.unique_hint = value.parse::()?; + } + + "@copyrat-match-fg" => self.colors.match_fg = ui::colors::parse_color(value)?, + "@copyrat-match-bg" => self.colors.match_bg = ui::colors::parse_color(value)?, + "@copyrat-focused-fg" => self.colors.focused_fg = ui::colors::parse_color(value)?, + "@copyrat-focused-bg" => self.colors.focused_bg = ui::colors::parse_color(value)?, + "@copyrat-hint-fg" => self.colors.hint_fg = ui::colors::parse_color(value)?, + "@copyrat-hint-bg" => self.colors.hint_bg = ui::colors::parse_color(value)?, + + "@copyrat-hint-alignment" => { + self.hint_alignment = ui::HintAlignment::from_str(&value)? + } + "@copyrat-hint-style" => self.hint_style = Some(HintStyleCli::from_str(&value)?), + + // Ignore unknown options. + _ => (), + } + } + + Ok(()) + } +} diff --git a/src/config/mod.rs b/src/config/mod.rs new file mode 100644 index 0000000..b834c28 --- /dev/null +++ b/src/config/mod.rs @@ -0,0 +1,3 @@ +mod core; + +pub use self::core::{CliOpt, HintStyleCli}; diff --git a/src/lib.rs b/src/lib.rs index 7ef5227..42535d6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,22 +1,15 @@ pub mod comm; +pub mod config; pub mod error; pub mod textbuf; pub mod ui; -use clap::Clap; -use std::collections::HashMap; -use std::path; -use std::str::FromStr; - -use crate::textbuf::alphabet; -use crate::textbuf::regexes; - /// Run copyrat on an input string `buffer`, configured by `Opt`. /// /// # Note /// /// Maybe the decision to take ownership of the buffer is a bit bold. -pub fn run(buffer: String, opt: &CliOpt) -> Option { +pub fn run(buffer: String, opt: &config::CliOpt) -> Option { let mut model = textbuf::Model::new( &buffer, &opt.alphabet, @@ -29,10 +22,10 @@ pub fn run(buffer: String, opt: &CliOpt) -> Option { let hint_style = match &opt.hint_style { None => None, Some(style) => match style { - HintStyleCli::Bold => Some(ui::HintStyle::Bold), - HintStyleCli::Italic => Some(ui::HintStyle::Italic), - HintStyleCli::Underline => Some(ui::HintStyle::Underline), - HintStyleCli::Surround => { + config::HintStyleCli::Bold => Some(ui::HintStyle::Bold), + config::HintStyleCli::Italic => Some(ui::HintStyle::Italic), + config::HintStyleCli::Underline => Some(ui::HintStyle::Underline), + config::HintStyleCli::Surround => { let (open, close) = opt.hint_surroundings; Some(ui::HintStyle::Surround(open, close)) } @@ -57,151 +50,3 @@ pub fn run(buffer: String, opt: &CliOpt) -> Option { selection } - -/// Main configuration, parsed from command line. -#[derive(Clap, Debug)] -#[clap(author, about, version)] -pub struct CliOpt { - /// Alphabet to draw hints from. - /// - /// Possible values are "{A}", "{A}-homerow", "{A}-left-hand", - /// "{A}-right-hand", where "{A}" is one of "qwerty", "azerty", "qwertz" - /// "dvorak", "colemak". - /// - /// # Examples - /// - /// "qwerty", "dvorak-homerow", "azerty-right-hand". - #[clap(short = 'k', long, default_value = "dvorak", - parse(try_from_str = alphabet::parse_alphabet))] - alphabet: alphabet::Alphabet, - - /// Use all available regex patterns. - #[clap(short = 'A', long = "--all-patterns")] - use_all_patterns: bool, - - /// Pattern names to use ("email", ... see doc). - #[clap(short = 'x', long = "--pattern-name", parse(try_from_str = regexes::parse_pattern_name))] - named_patterns: Vec, - - /// Additional regex patterns ("foo*bar", etc). - #[clap(short = 'X', long = "--custom-pattern")] - custom_patterns: Vec, - - /// Assign hints starting from the bottom of the screen. - #[clap(short, long)] - reverse: bool, - - /// Keep the same hint for identical matches. - #[clap(short, long)] - unique_hint: bool, - - #[clap(flatten)] - colors: ui::colors::UiColors, - - /// Align hint with its match. - #[clap(long, arg_enum, default_value = "leading")] - hint_alignment: ui::HintAlignment, - - /// Move focus back to first/last match. - #[clap(long)] - focus_wrap_around: bool, - - /// Optional hint styling. - /// - /// Underline or surround the hint for increased visibility. - /// If not provided, only the hint colors will be used. - #[clap(short = 's', long, arg_enum)] - hint_style: Option, - - /// Chars surrounding each hint, used with `Surround` style. - #[clap(long, default_value = "{}", - parse(try_from_str = parse_chars))] - hint_surroundings: (char, char), - - /// Optional target path where to store the selected matches. - #[clap(short = 'o', long = "output", parse(from_os_str))] - pub target_path: Option, - - /// Describes if the uppercased marker should be added to the output, - /// indicating if hint key was uppercased. This is only used by - /// tmux-copyrat, so it is hidden (skipped) from the CLI. - #[clap(skip)] - pub uppercased_marker: bool, -} - -/// Type introduced due to parsing limitation, -/// as we cannot directly parse into ui::HintStyle. -#[derive(Debug, Clap)] -enum HintStyleCli { - Bold, - Italic, - Underline, - Surround, -} - -impl FromStr for HintStyleCli { - type Err = error::ParseError; - - fn from_str(s: &str) -> Result { - match s { - "leading" => Ok(HintStyleCli::Underline), - "trailing" => Ok(HintStyleCli::Surround), - _ => Err(error::ParseError::ExpectedString(String::from( - "underline or surround", - ))), - } - } -} - -/// Try to parse a `&str` into a tuple of `char`s. -fn parse_chars(src: &str) -> Result<(char, char), error::ParseError> { - if src.len() != 2 { - return Err(error::ParseError::ExpectedSurroundingPair); - } - - let chars: Vec = src.chars().collect(); - Ok((chars[0], chars[1])) -} - -impl CliOpt { - /// Try parsing provided options, and update self with the valid values. - pub fn merge_map( - &mut self, - options: &HashMap, - ) -> Result<(), error::ParseError> { - for (name, value) in options { - match name.as_ref() { - "@copyrat-alphabet" => { - self.alphabet = alphabet::parse_alphabet(value)?; - } - "@copyrat-pattern-name" => { - self.named_patterns = vec![regexes::parse_pattern_name(value)?] - } - "@copyrat-custom-pattern" => self.custom_patterns = vec![String::from(value)], - "@copyrat-reverse" => { - self.reverse = value.parse::()?; - } - "@copyrat-unique-hint" => { - self.unique_hint = value.parse::()?; - } - - "@copyrat-match-fg" => self.colors.match_fg = ui::colors::parse_color(value)?, - "@copyrat-match-bg" => self.colors.match_bg = ui::colors::parse_color(value)?, - "@copyrat-focused-fg" => self.colors.focused_fg = ui::colors::parse_color(value)?, - "@copyrat-focused-bg" => self.colors.focused_bg = ui::colors::parse_color(value)?, - "@copyrat-hint-fg" => self.colors.hint_fg = ui::colors::parse_color(value)?, - "@copyrat-hint-bg" => self.colors.hint_bg = ui::colors::parse_color(value)?, - - "@copyrat-hint-alignment" => { - self.hint_alignment = ui::HintAlignment::from_str(&value)? - } - "@copyrat-hint-style" => self.hint_style = Some(HintStyleCli::from_str(&value)?), - - // Ignore unknown options. - _ => (), - } - } - - Ok(()) - } -}