mirror of
https://github.com/TECHNOFAB11/tmux-copyrat.git
synced 2025-12-13 00:20:08 +01:00
refactor: use custom Result
This commit is contained in:
parent
077d16311e
commit
7d1bb69c6f
8 changed files with 34 additions and 33 deletions
|
|
@ -1,11 +1,11 @@
|
||||||
use copyrat::{
|
use copyrat::{
|
||||||
config::extended::{ConfigExt, OutputDestination},
|
config::extended::{ConfigExt, OutputDestination},
|
||||||
error, tmux,
|
tmux,
|
||||||
ui::Selection,
|
ui::Selection,
|
||||||
|
Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
///
|
fn main() -> Result<()> {
|
||||||
fn main() -> Result<(), error::ParseError> {
|
|
||||||
let config = ConfigExt::initialize()?;
|
let config = ConfigExt::initialize()?;
|
||||||
|
|
||||||
// Identify active pane and capture its content.
|
// Identify active pane and capture its content.
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use clap::{ArgEnum, Parser};
|
||||||
use crate::{
|
use crate::{
|
||||||
error::ParseError,
|
error::ParseError,
|
||||||
textbuf::{alphabet, regexes},
|
textbuf::{alphabet, regexes},
|
||||||
ui,
|
ui, Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Main configuration, parsed from command line.
|
/// Main configuration, parsed from command line.
|
||||||
|
|
@ -78,7 +78,7 @@ pub enum HintStyleArg {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Try to parse a `&str` into a tuple of `char`s.
|
/// Try to parse a `&str` into a tuple of `char`s.
|
||||||
fn parse_chars(src: &str) -> Result<(char, char), ParseError> {
|
fn parse_chars(src: &str) -> Result<(char, char)> {
|
||||||
if src.chars().count() != 2 {
|
if src.chars().count() != 2 {
|
||||||
return Err(ParseError::ExpectedSurroundingPair);
|
return Err(ParseError::ExpectedSurroundingPair);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use std::fmt;
|
||||||
use clap::{ArgEnum, Parser};
|
use clap::{ArgEnum, Parser};
|
||||||
|
|
||||||
use super::basic;
|
use super::basic;
|
||||||
use crate::{error::ParseError, textbuf::alphabet, tmux, ui};
|
use crate::{error::ParseError, textbuf::alphabet, tmux, ui, Result};
|
||||||
|
|
||||||
/// Extended configuration for handling Tmux-specific configuration (options
|
/// Extended configuration for handling Tmux-specific configuration (options
|
||||||
/// and outputs). This is only used by `tmux-copyrat` and parsed from command
|
/// and outputs). This is only used by `tmux-copyrat` and parsed from command
|
||||||
|
|
@ -52,7 +52,7 @@ pub struct ConfigExt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConfigExt {
|
impl ConfigExt {
|
||||||
pub fn initialize() -> Result<ConfigExt, ParseError> {
|
pub fn initialize() -> Result<ConfigExt> {
|
||||||
let mut config_ext = ConfigExt::parse();
|
let mut config_ext = ConfigExt::parse();
|
||||||
|
|
||||||
if !config_ext.ignore_tmux_options {
|
if !config_ext.ignore_tmux_options {
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ pub mod textbuf;
|
||||||
pub mod tmux;
|
pub mod tmux;
|
||||||
pub mod ui;
|
pub mod ui;
|
||||||
|
|
||||||
|
pub type Result<T> = std::result::Result<T, error::ParseError>;
|
||||||
|
|
||||||
/// Run copyrat on an input string `buffer`, configured by `Opt`.
|
/// Run copyrat on an input string `buffer`, configured by `Opt`.
|
||||||
///
|
///
|
||||||
/// # Note
|
/// # Note
|
||||||
|
|
@ -11,7 +13,7 @@ pub mod ui;
|
||||||
/// Maybe the decision to take ownership of the buffer is a bit bold.
|
/// Maybe the decision to take ownership of the buffer is a bit bold.
|
||||||
pub fn run(lines: &[&str], opt: &config::basic::Config) -> Option<ui::Selection> {
|
pub fn run(lines: &[&str], opt: &config::basic::Config) -> Option<ui::Selection> {
|
||||||
let model = textbuf::Model::new(
|
let model = textbuf::Model::new(
|
||||||
&lines,
|
lines,
|
||||||
&opt.alphabet,
|
&opt.alphabet,
|
||||||
opt.use_all_patterns,
|
opt.use_all_patterns,
|
||||||
&opt.named_patterns,
|
&opt.named_patterns,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::error;
|
use crate::{error::ParseError, Result};
|
||||||
|
|
||||||
/// Catalog of available alphabets.
|
/// Catalog of available alphabets.
|
||||||
///
|
///
|
||||||
|
|
@ -41,7 +41,7 @@ const ALPHABETS: [(&str, &str); 21] = [
|
||||||
/// Letters 'n' and 'N' are systematically removed to prevent conflict with
|
/// Letters 'n' and 'N' are systematically removed to prevent conflict with
|
||||||
/// navigation keys (arrows and 'n' 'N'). Letters 'y' and 'Y' are also removed
|
/// navigation keys (arrows and 'n' 'N'). Letters 'y' and 'Y' are also removed
|
||||||
/// to prevent conflict with yank/copy.
|
/// to prevent conflict with yank/copy.
|
||||||
pub fn parse_alphabet(src: &str) -> Result<Alphabet, error::ParseError> {
|
pub fn parse_alphabet(src: &str) -> Result<Alphabet> {
|
||||||
let alphabet_pair = ALPHABETS.iter().find(|&(name, _letters)| name == &src);
|
let alphabet_pair = ALPHABETS.iter().find(|&(name, _letters)| name == &src);
|
||||||
|
|
||||||
match alphabet_pair {
|
match alphabet_pair {
|
||||||
|
|
@ -49,7 +49,7 @@ pub fn parse_alphabet(src: &str) -> Result<Alphabet, error::ParseError> {
|
||||||
let letters = letters.replace(&['n', 'N', 'y', 'Y'][..], "");
|
let letters = letters.replace(&['n', 'N', 'y', 'Y'][..], "");
|
||||||
Ok(Alphabet(letters))
|
Ok(Alphabet(letters))
|
||||||
}
|
}
|
||||||
None => Err(error::ParseError::UnknownAlphabet),
|
None => Err(ParseError::UnknownAlphabet),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
//!
|
//!
|
||||||
//! All patterns must have one capture group. The first group is used.
|
//! All patterns must have one capture group. The first group is used.
|
||||||
|
|
||||||
use crate::error;
|
use crate::{error::ParseError, Result};
|
||||||
|
|
||||||
pub(super) const EXCLUDE_PATTERNS: [(&str, &str); 1] =
|
pub(super) const EXCLUDE_PATTERNS: [(&str, &str); 1] =
|
||||||
[("ansi_colors", r"[[:cntrl:]]\[([0-9]{1,2};)?([0-9]{1,2})?m")];
|
[("ansi_colors", r"[[:cntrl:]]\[([0-9]{1,2};)?([0-9]{1,2})?m")];
|
||||||
|
|
@ -51,9 +51,9 @@ pub(super) const PATTERNS: [(&str, &str); 20] = [
|
||||||
pub struct NamedPattern(pub String, pub String);
|
pub struct NamedPattern(pub String, pub String);
|
||||||
|
|
||||||
/// Parse a name string into `NamedPattern`, used during CLI parsing.
|
/// Parse a name string into `NamedPattern`, used during CLI parsing.
|
||||||
pub(crate) fn parse_pattern_name(src: &str) -> Result<NamedPattern, error::ParseError> {
|
pub(crate) fn parse_pattern_name(src: &str) -> Result<NamedPattern> {
|
||||||
match PATTERNS.iter().find(|&(name, _pattern)| name == &src) {
|
match PATTERNS.iter().find(|&(name, _pattern)| name == &src) {
|
||||||
Some((name, pattern)) => Ok(NamedPattern(name.to_string(), pattern.to_string())),
|
Some((name, pattern)) => Ok(NamedPattern(name.to_string(), pattern.to_string())),
|
||||||
None => Err(error::ParseError::UnknownPatternName),
|
None => Err(ParseError::UnknownPatternName),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
30
src/tmux.rs
30
src/tmux.rs
|
|
@ -3,13 +3,14 @@
|
||||||
//! The main use cases are running Tmux commands & parsing Tmux panes
|
//! The main use cases are running Tmux commands & parsing Tmux panes
|
||||||
//! information.
|
//! information.
|
||||||
|
|
||||||
use regex::Regex;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
use crate::config::extended::CaptureRegion;
|
use crate::config::extended::CaptureRegion;
|
||||||
use crate::error::ParseError;
|
use crate::{error::ParseError, Result};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct Pane {
|
pub struct Pane {
|
||||||
|
|
@ -44,7 +45,7 @@ impl FromStr for Pane {
|
||||||
///
|
///
|
||||||
/// For definitions, look at `Pane` type,
|
/// For definitions, look at `Pane` type,
|
||||||
/// and at the tmux man page for definitions.
|
/// and at the tmux man page for definitions.
|
||||||
fn from_str(src: &str) -> Result<Self, Self::Err> {
|
fn from_str(src: &str) -> std::result::Result<Self, Self::Err> {
|
||||||
let items: Vec<&str> = src.split(':').collect();
|
let items: Vec<&str> = src.split(':').collect();
|
||||||
assert_eq!(items.len(), 5, "tmux should have returned 5 items per line");
|
assert_eq!(items.len(), 5, "tmux should have returned 5 items per line");
|
||||||
|
|
||||||
|
|
@ -98,7 +99,7 @@ impl Pane {
|
||||||
/// scrolled up by 3 lines. It is necessarily in copy mode. Its start line
|
/// scrolled up by 3 lines. It is necessarily in copy mode. Its start line
|
||||||
/// index is `-3`. The index of the last line is `(40-1) - 3 = 36`.
|
/// index is `-3`. The index of the last line is `(40-1) - 3 = 36`.
|
||||||
///
|
///
|
||||||
pub fn capture(&self, region: &CaptureRegion) -> Result<String, ParseError> {
|
pub fn capture(&self, region: &CaptureRegion) -> Result<String> {
|
||||||
let mut args_str = format!("capture-pane -t {pane_id} -J -p", pane_id = self.id);
|
let mut args_str = format!("capture-pane -t {pane_id} -J -p", pane_id = self.id);
|
||||||
|
|
||||||
let region_str = match region {
|
let region_str = match region {
|
||||||
|
|
@ -133,7 +134,7 @@ impl FromStr for PaneId {
|
||||||
|
|
||||||
/// Parse into PaneId. The `&str` must be start with '%'
|
/// Parse into PaneId. The `&str` must be start with '%'
|
||||||
/// followed by a `u16`.
|
/// followed by a `u16`.
|
||||||
fn from_str(src: &str) -> Result<Self, Self::Err> {
|
fn from_str(src: &str) -> std::result::Result<Self, Self::Err> {
|
||||||
if !src.starts_with('%') {
|
if !src.starts_with('%') {
|
||||||
return Err(ParseError::ExpectedPaneIdMarker);
|
return Err(ParseError::ExpectedPaneIdMarker);
|
||||||
}
|
}
|
||||||
|
|
@ -156,7 +157,7 @@ impl fmt::Display for PaneId {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a list of `Pane` from the current tmux session.
|
/// Returns a list of `Pane` from the current tmux session.
|
||||||
pub fn available_panes() -> Result<Vec<Pane>, ParseError> {
|
pub fn available_panes() -> Result<Vec<Pane>> {
|
||||||
let args = vec![
|
let args = vec![
|
||||||
"list-panes",
|
"list-panes",
|
||||||
"-F",
|
"-F",
|
||||||
|
|
@ -165,9 +166,9 @@ pub fn available_panes() -> Result<Vec<Pane>, ParseError> {
|
||||||
|
|
||||||
let output = duct::cmd("tmux", &args).read()?;
|
let output = duct::cmd("tmux", &args).read()?;
|
||||||
|
|
||||||
// Each call to `Pane::parse` returns a `Result<Pane, _>`. All results
|
// Each call to `Pane::parse` returns a `Result<Pane>`. All results
|
||||||
// are collected into a Result<Vec<Pane>, _>, thanks to `collect()`.
|
// are collected into a Result<Vec<Pane>>, thanks to `collect()`.
|
||||||
let result: Result<Vec<Pane>, ParseError> = output
|
let result: Result<Vec<Pane>> = output
|
||||||
.trim_end() // trim last '\n' as it would create an empty line
|
.trim_end() // trim last '\n' as it would create an empty line
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.map(|line| Pane::from_str(line))
|
.map(|line| Pane::from_str(line))
|
||||||
|
|
@ -182,7 +183,7 @@ pub fn available_panes() -> Result<Vec<Pane>, ParseError> {
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
/// ```get_options("@copyrat-")```
|
/// ```get_options("@copyrat-")```
|
||||||
pub fn get_options(prefix: &str) -> Result<HashMap<String, String>, ParseError> {
|
pub fn get_options(prefix: &str) -> Result<HashMap<String, String>> {
|
||||||
let output = duct::cmd!("tmux", "show-options", "-g").read()?;
|
let output = duct::cmd!("tmux", "show-options", "-g").read()?;
|
||||||
let lines: Vec<&str> = output.split('\n').collect();
|
let lines: Vec<&str> = output.split('\n').collect();
|
||||||
|
|
||||||
|
|
@ -205,7 +206,7 @@ pub fn get_options(prefix: &str) -> Result<HashMap<String, String>, ParseError>
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ask tmux to swap the current Pane with the target_pane (uses Tmux format).
|
/// Ask tmux to swap the current Pane with the target_pane (uses Tmux format).
|
||||||
pub fn swap_pane_with(target_pane: &str) -> Result<(), ParseError> {
|
pub fn swap_pane_with(target_pane: &str) -> Result<()> {
|
||||||
// -Z: keep the window zoomed if it was zoomed.
|
// -Z: keep the window zoomed if it was zoomed.
|
||||||
duct::cmd!("tmux", "swap-pane", "-Z", "-s", target_pane).run()?;
|
duct::cmd!("tmux", "swap-pane", "-Z", "-s", target_pane).run()?;
|
||||||
|
|
||||||
|
|
@ -214,16 +215,13 @@ pub fn swap_pane_with(target_pane: &str) -> Result<(), ParseError> {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Pane;
|
use super::*;
|
||||||
use super::PaneId;
|
|
||||||
use crate::error;
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_pass() {
|
fn test_parse_pass() {
|
||||||
let output = vec!["%52:false:62:3:false", "%53:false:23::true"];
|
let output = vec!["%52:false:62:3:false", "%53:false:23::true"];
|
||||||
let panes: Result<Vec<Pane>, error::ParseError> =
|
let panes: Result<Vec<Pane>> = output.iter().map(|&line| Pane::from_str(line)).collect();
|
||||||
output.iter().map(|&line| Pane::from_str(line)).collect();
|
|
||||||
let panes = panes.expect("Could not parse tmux panes");
|
let panes = panes.expect("Could not parse tmux panes");
|
||||||
|
|
||||||
let expected = vec![
|
let expected = vec![
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
use crate::error;
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use termion::color;
|
use termion::color;
|
||||||
|
|
||||||
pub fn parse_color(src: &str) -> Result<Box<dyn color::Color>, error::ParseError> {
|
use crate::{error::ParseError, Result};
|
||||||
|
|
||||||
|
pub fn parse_color(src: &str) -> Result<Box<dyn color::Color>> {
|
||||||
match src {
|
match src {
|
||||||
"black" => Ok(Box::new(color::Black)),
|
"black" => Ok(Box::new(color::Black)),
|
||||||
"red" => Ok(Box::new(color::Red)),
|
"red" => Ok(Box::new(color::Red)),
|
||||||
|
|
@ -21,7 +22,7 @@ pub fn parse_color(src: &str) -> Result<Box<dyn color::Color>, error::ParseError
|
||||||
"bright-cyan" | "brightcyan" => Ok(Box::new(color::LightCyan)),
|
"bright-cyan" | "brightcyan" => Ok(Box::new(color::LightCyan)),
|
||||||
"bright-white" | "brightwhite" => Ok(Box::new(color::LightWhite)),
|
"bright-white" | "brightwhite" => Ok(Box::new(color::LightWhite)),
|
||||||
"none" => Ok(Box::new(color::Reset)),
|
"none" => Ok(Box::new(color::Reset)),
|
||||||
_ => Err(error::ParseError::UnknownColor),
|
_ => Err(ParseError::UnknownColor),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue