Minor code base improvements (#767)

This commit is contained in:
Denis Isidoro 2022-07-29 19:25:36 -03:00 committed by GitHub
parent 8b78d54863
commit 9d862344e6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 242 additions and 233 deletions

View file

@ -1,5 +1,5 @@
use crate::prelude::*;
use std::process::{self, Command, Stdio};
use std::process::{Command, Stdio};
lazy_static! {
pub static ref VAR_TLDR_REGEX: Regex = Regex::new(r"\{\{(.*?)\}\}").expect("Invalid regex");

View file

@ -1,14 +1,14 @@
use super::extractor;
use crate::common::clipboard;
use crate::common::fs;
use crate::common::shell;
use crate::common::shell::ShellSpawnError;
use crate::config::Action;
use crate::deser;
use crate::env_var;
use crate::finder::structures::{Opts as FinderOpts, SuggestionType};
use crate::prelude::*;
use crate::serializer;
use crate::structures::cheat::{Suggestion, VariableMap};
use crate::structures::item::Item;
use shell::EOF;
use std::process::Stdio;
@ -145,10 +145,7 @@ fn unique_result_count(results: &[&str]) -> usize {
fn replace_variables_from_snippet(snippet: &str, tags: &str, variables: VariableMap) -> Result<String> {
let mut interpolated_snippet = String::from(snippet);
let variables_found: Vec<&str> = serializer::VAR_REGEX
.find_iter(snippet)
.map(|m| m.as_str())
.collect();
let variables_found: Vec<&str> = deser::VAR_REGEX.find_iter(snippet).map(|m| m.as_str()).collect();
let variable_count = unique_result_count(&variables_found);
for bracketed_variable_name in variables_found {
@ -187,11 +184,20 @@ pub fn with_absolute_path(snippet: String) -> String {
}
pub fn act(
extractions: Result<extractor::Output>,
extractions: Result<(&str, Item)>,
files: Vec<String>,
variables: Option<VariableMap>,
) -> Result<()> {
let (key, tags, comment, snippet, file_index) = extractions.unwrap();
let (
key,
Item {
tags,
comment,
snippet,
file_index,
..
},
) = extractions.unwrap();
if key == "ctrl-o" {
edit::edit_file(Path::new(&files[file_index.expect("No files found")]))
@ -205,13 +211,13 @@ pub fn act(
let interpolated_snippet = {
let mut s = replace_variables_from_snippet(
snippet,
tags,
&snippet,
&tags,
variables.expect("No variables received from finder"),
)
.context("Failed to replace variables from snippet")?;
s = with_absolute_path(s);
s = serializer::with_new_lines(s);
s = deser::with_new_lines(s);
s
};

View file

@ -1,27 +0,0 @@
use crate::prelude::*;
use crate::serializer;
pub type Output<'a> = (&'a str, &'a str, &'a str, &'a str, Option<usize>);
pub fn extract_from_selections(raw_snippet: &str, is_single: bool) -> Result<Output> {
let mut lines = raw_snippet.split('\n');
let key = if is_single {
"enter"
} else {
lines
.next()
.context("Key was promised but not present in `selections`")?
};
let mut parts = lines
.next()
.context("No more parts in `selections`")?
.split(serializer::DELIMITER)
.skip(3);
let tags = parts.next().unwrap_or("");
let comment = parts.next().unwrap_or("");
let snippet = parts.next().unwrap_or("");
let file_index = parts.next().unwrap_or("").parse().ok();
Ok((key, tags, comment, snippet, file_index))
}

View file

@ -1,8 +1,8 @@
mod actor;
mod extractor;
use crate::clients::cheatsh;
use crate::clients::{cheatsh, tldr};
use crate::config::Source;
use crate::deser;
use crate::filesystem;
use crate::finder::structures::Opts as FinderOpts;
use crate::parser::Parser;
@ -32,7 +32,7 @@ pub fn init(fetcher: Box<dyn Fetcher>) -> Result<()> {
})
.context("Failed getting selection and variables from finder")?;
let extractions = extractor::extract_from_selections(&raw_selection, config.best_match());
let extractions = deser::terminal::read(&raw_selection, config.best_match());
if extractions.is_err() {
return init(fetcher);
@ -51,7 +51,7 @@ pub fn get_fetcher() -> Result<Box<dyn Fetcher>> {
Ok(fetcher)
}
Source::Tldr(query) => {
let lines = cheatsh::call(&query)?;
let lines = tldr::call(&query)?;
let fetcher = Box::new(StaticFetcher::new(lines));
Ok(fetcher)
}

View file

@ -8,7 +8,7 @@ use crate::prelude::*;
use clap::Args;
use clap::Parser;
const FUNC_POSSIBLE_VALUES: &[&str] = &[
const POSSIBLE_VALUES: &[&str] = &[
"url::open",
"welcome",
"widget::last_command",
@ -43,7 +43,7 @@ pub enum Func {
#[derive(Debug, Clone, Args)]
pub struct Input {
/// Function name (example: "url::open")
#[clap(possible_values = FUNC_POSSIBLE_VALUES, ignore_case = true)]
#[clap(possible_values = POSSIBLE_VALUES, ignore_case = true)]
pub func: Func,
/// List of arguments (example: "https://google.com")
pub args: Vec<String>,
@ -63,3 +63,10 @@ impl Runnable for Input {
}
}
}
#[test]
fn test_possible_values() {
for v in POSSIBLE_VALUES {
assert!(Func::from_str(v).is_ok())
}
}

View file

@ -3,7 +3,7 @@ use clap::Args;
use crate::filesystem;
use crate::prelude::*;
const INFO_POSSIBLE_VALUES: &[&str] = &["cheats-example", "cheats-path", "config-path", "config-example"];
const POSSIBLE_VALUES: &[&str] = &["cheats-example", "cheats-path", "config-path", "config-example"];
impl FromStr for Info {
type Err = &'static str;
@ -21,7 +21,7 @@ impl FromStr for Info {
#[derive(Debug, Clone, Args)]
pub struct Input {
#[clap(possible_values = INFO_POSSIBLE_VALUES, ignore_case = true)]
#[clap(possible_values = POSSIBLE_VALUES, ignore_case = true)]
pub info: Info,
}
@ -46,3 +46,10 @@ impl Runnable for Input {
Ok(())
}
}
#[test]
fn test_possible_values() {
for v in POSSIBLE_VALUES {
assert!(Info::from_str(v).is_ok())
}
}

View file

@ -1,5 +1,5 @@
use crate::deser;
use crate::prelude::*;
use crate::serializer;
use clap::Args;
use crossterm::style::{style, Stylize};
use std::process;
@ -14,7 +14,7 @@ pub struct Input {
}
fn extract_elements(argstr: &str) -> Result<(&str, &str, &str)> {
let mut parts = argstr.split(serializer::DELIMITER).skip(3);
let mut parts = argstr.split(deser::terminal::DELIMITER).skip(3);
let tags = parts.next().context("No `tags` element provided.")?;
let comment = parts.next().context("No `comment` element provided.")?;
let snippet = parts.next().context("No `snippet` element provided.")?;
@ -31,7 +31,7 @@ impl Runnable for Input {
"{comment} {tags} \n{snippet}",
comment = style(comment).with(CONFIG.comment_color()),
tags = style(format!("[{}]", tags)).with(CONFIG.tag_color()),
snippet = style(serializer::fix_newlines(snippet)).with(CONFIG.snippet_color()),
snippet = style(deser::fix_newlines(snippet)).with(CONFIG.snippet_color()),
);
process::exit(0)

View file

@ -1,7 +1,7 @@
use crate::deser;
use crate::env_var;
use crate::finder;
use crate::prelude::*;
use crate::serializer;
use clap::Args;
use crossterm::style::style;
use crossterm::style::Stylize;
@ -49,10 +49,7 @@ impl Runnable for Input {
let bracketed_variables: Vec<&str> = {
if snippet.contains(&bracketed_current_variable) {
serializer::VAR_REGEX
.find_iter(&snippet)
.map(|m| m.as_str())
.collect()
deser::VAR_REGEX.find_iter(&snippet).map(|m| m.as_str()).collect()
} else {
iter::once(&bracketed_current_variable)
.map(|s| s.as_str())
@ -98,7 +95,7 @@ impl Runnable for Input {
);
}
println!("{snippet}", snippet = serializer::fix_newlines(&colored_snippet));
println!("{snippet}", snippet = deser::fix_newlines(&colored_snippet));
println!("{variables}", variables = variables);
process::exit(0)

View file

@ -3,7 +3,7 @@ use clap::Args;
use crate::common::shell::Shell;
use crate::prelude::*;
const WIDGET_POSSIBLE_VALUES: &[&str] = &["bash", "zsh", "fish", "elvish"];
const POSSIBLE_VALUES: &[&str] = &["bash", "zsh", "fish", "elvish"];
impl FromStr for Shell {
type Err = &'static str;
@ -21,7 +21,7 @@ impl FromStr for Shell {
#[derive(Debug, Clone, Args)]
pub struct Input {
#[clap(possible_values = WIDGET_POSSIBLE_VALUES, ignore_case = true, default_value = "bash")]
#[clap(possible_values = POSSIBLE_VALUES, ignore_case = true, default_value = "bash")]
pub shell: Shell,
}
@ -41,3 +41,10 @@ impl Runnable for Input {
Ok(())
}
}
#[test]
fn test_possible_values() {
for v in POSSIBLE_VALUES {
assert!(Shell::from_str(v).is_ok())
}
}

View file

@ -2,11 +2,11 @@ use crate::commands::core::get_fetcher;
use crate::common::shell::{self, ShellSpawnError};
use crate::finder::structures::Opts as FinderOpts;
use crate::parser::Parser;
use crate::{prelude::*, serializer};
use crate::{deser, prelude::*};
use std::io::{self, Write};
pub fn main() -> Result<()> {
let config = &CONFIG;
let _config = &CONFIG;
let _opts = FinderOpts::snippet_default();
let fetcher = get_fetcher()?;
@ -22,7 +22,7 @@ pub fn main() -> Result<()> {
let variables = parser.variables;
let item_str = String::from_utf8(buf)?;
let item = serializer::raycast_deser(&item_str)?;
let item = deser::raycast::read(&item_str)?;
dbg!(&item);
let x = variables.get_suggestion(&item.tags, "local_branch").expect("foo");
@ -49,7 +49,7 @@ pub fn main() -> Result<()> {
}
pub fn _main0() -> Result<()> {
let config = &CONFIG;
let _config = &CONFIG;
let fetcher = get_fetcher()?;

View file

@ -1,10 +1,8 @@
use crate::commands;
use crate::finder::FinderChoice;
use crate::finder::{self, FinderChoice};
use crate::prelude::*;
use clap::{crate_version, AppSettings, Parser, Subcommand};
const FINDER_POSSIBLE_VALUES: &[&str] = &["fzf", "skim"];
#[derive(Debug, Parser)]
#[clap(after_help = "\x1b[0;33mMORE INFO:\x1b[0;0m
Please refer to \x1b[0;32mhttps://github.com/denisidoro/navi\x1b[0;0m
@ -80,7 +78,7 @@ pub(super) struct ClapConfig {
pub fzf_overrides_var: Option<String>,
/// Finder application to use
#[clap(long, possible_values = FINDER_POSSIBLE_VALUES, ignore_case = true)]
#[clap(long, possible_values = finder::POSSIBLE_VALUES, ignore_case = true)]
pub finder: Option<FinderChoice>,
#[clap(subcommand)]
@ -127,38 +125,3 @@ pub enum Action {
Print,
Execute,
}
/*
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_widget_possible_values() {
for v in WIDGET_POSSIBLE_VALUES {
assert!(Shell::from_str(v).is_ok())
}
}
#[test]
fn test_info_possible_values() {
for v in INFO_POSSIBLE_VALUES {
assert!(Info::from_str(v).is_ok())
}
}
#[test]
fn test_func_possible_values() {
for v in FUNC_POSSIBLE_VALUES {
assert!(Func::from_str(v).is_ok())
}
}
#[test]
fn test_finder_possible_values() {
for v in FINDER_POSSIBLE_VALUES {
assert!(FinderChoice::from_str(v).is_ok())
}
}
}
*/

34
src/deser/mod.rs Normal file
View file

@ -0,0 +1,34 @@
use crate::prelude::*;
pub mod raycast;
pub mod terminal;
const NEWLINE_ESCAPE_CHAR: char = '\x15';
pub const LINE_SEPARATOR: &str = " \x15 ";
lazy_static! {
pub static ref NEWLINE_REGEX: Regex = Regex::new(r"\\\s+").expect("Invalid regex");
pub static ref VAR_REGEX: Regex = Regex::new(r"\\?<(\w[\w\d\-_]*)>").expect("Invalid regex");
}
pub fn with_new_lines(txt: String) -> String {
txt.replace(LINE_SEPARATOR, "\n")
}
pub fn fix_newlines(txt: &str) -> String {
if txt.contains(NEWLINE_ESCAPE_CHAR) {
(*NEWLINE_REGEX)
.replace_all(txt.replace(LINE_SEPARATOR, " ").as_str(), "")
.to_string()
} else {
txt.to_string()
}
}
fn limit_str(text: &str, length: usize) -> String {
if text.len() > length {
format!("{}", text.chars().take(length - 1).collect::<String>())
} else {
format!("{:width$}", text, width = length)
}
}

51
src/deser/raycast.rs Normal file
View file

@ -0,0 +1,51 @@
use super::*;
use crate::structures::item::Item;
const FIELD_SEP_ESCAPE_CHAR: char = '\x16';
pub fn write(item: &Item) -> String {
format!(
"{hash}{delimiter}{tags}{delimiter}{comment}{delimiter}{icon}{delimiter}{snippet}\n",
hash = item.hash(),
tags = item.tags,
comment = item.comment,
delimiter = FIELD_SEP_ESCAPE_CHAR,
icon = item.icon.clone().unwrap_or_default(),
snippet = &item.snippet.trim_end_matches(LINE_SEPARATOR),
)
}
pub fn read(line: &str) -> Result<Item> {
let mut parts = line.split(FIELD_SEP_ESCAPE_CHAR);
let hash: u64 = parts
.next()
.context("no hash")?
.parse()
.context("hash not a u64")?;
let tags = parts.next().context("no tags")?.into();
let comment = parts.next().context("no comment")?.into();
let icon_str = parts.next().context("no icon")?;
let snippet = parts.next().context("no snippet")?.into();
let icon = if icon_str.is_empty() {
None
} else {
Some(icon_str.into())
};
let item = Item {
tags,
comment,
icon,
snippet,
..Default::default()
};
if item.hash() != hash {
dbg!(&item.hash());
dbg!(hash);
Err(anyhow!("Incorrect hash"))
} else {
Ok(item)
}
}

79
src/deser/terminal.rs Normal file
View file

@ -0,0 +1,79 @@
use super::*;
use crate::common::terminal;
use crate::structures::item::Item;
use crossterm::style::{style, Stylize};
use std::cmp::max;
pub fn get_widths() -> (usize, usize, usize) {
let width = terminal::width();
let tag_width_percentage = max(
CONFIG.tag_min_width(),
width * CONFIG.tag_width_percentage() / 100,
);
let comment_width_percentage = max(
CONFIG.comment_min_width(),
width * CONFIG.comment_width_percentage() / 100,
);
let snippet_width_percentage = max(
CONFIG.snippet_min_width(),
width * CONFIG.snippet_width_percentage() / 100,
);
(
usize::from(tag_width_percentage),
usize::from(comment_width_percentage),
usize::from(snippet_width_percentage),
)
}
pub const DELIMITER: &str = r" ";
lazy_static! {
pub static ref COLUMN_WIDTHS: (usize, usize, usize) = get_widths();
}
pub fn write(item: &Item) -> String {
let (tag_width_percentage, comment_width_percentage, snippet_width_percentage) = *COLUMN_WIDTHS;
format!(
"{tags_short}{delimiter}{comment_short}{delimiter}{snippet_short}{delimiter}{tags}{delimiter}{comment}{delimiter}{snippet}{delimiter}{file_index}{delimiter}\n",
tags_short = style(limit_str(&item.tags, tag_width_percentage)).with(CONFIG.tag_color()),
comment_short = style(limit_str(&item.comment, comment_width_percentage)).with(CONFIG.comment_color()),
snippet_short = style(limit_str(&fix_newlines(&item.snippet), snippet_width_percentage)).with(CONFIG.snippet_color()),
tags = item.tags,
comment = item.comment,
delimiter = DELIMITER,
snippet = &item.snippet.trim_end_matches(LINE_SEPARATOR),
file_index = item.file_index.unwrap_or(0),
)
}
pub fn read(raw_snippet: &str, is_single: bool) -> Result<(&str, Item)> {
let mut lines = raw_snippet.split('\n');
let key = if is_single {
"enter"
} else {
lines
.next()
.context("Key was promised but not present in `selections`")?
};
let mut parts = lines
.next()
.context("No more parts in `selections`")?
.split(DELIMITER)
.skip(3);
let tags = parts.next().unwrap_or("").into();
let comment = parts.next().unwrap_or("").into();
let snippet = parts.next().unwrap_or("").into();
let file_index = parts.next().unwrap_or("").parse().ok();
let item = Item {
tags,
comment,
snippet,
file_index,
..Default::default()
};
Ok((key, item))
}

View file

@ -1,6 +1,5 @@
use crate::deser;
use crate::prelude::*;
use crate::serializer;
use std::io::Write;
use std::process::{self, Output};
use std::process::{Command, Stdio};
@ -17,6 +16,8 @@ pub enum FinderChoice {
Skim,
}
pub const POSSIBLE_VALUES: &[&str] = &["fzf", "skim"];
impl FromStr for FinderChoice {
type Err = &'static str;
@ -78,7 +79,7 @@ impl FinderChoice {
"--with-nth",
"1,2,3",
"--delimiter",
serializer::DELIMITER.to_string().as_str(),
deser::terminal::DELIMITER.to_string().as_str(),
"--ansi",
"--bind",
format!("ctrl-j:down,ctrl-k:up{}", bindings).as_str(),
@ -189,3 +190,10 @@ impl FinderChoice {
Ok((output, return_value))
}
}
#[test]
fn test_possible_values() {
for v in POSSIBLE_VALUES {
assert!(FinderChoice::from_str(v).is_ok())
}
}

View file

@ -7,14 +7,13 @@ mod clients;
mod commands;
mod common;
mod config;
mod deser;
mod env_var;
mod filesystem;
mod finder;
mod parser;
mod prelude;
mod serializer;
mod structures;
mod terminal;
mod welcome;
pub use commands::handle;

View file

@ -1,7 +1,7 @@
use crate::common::fs;
use crate::deser;
use crate::finder::structures::{Opts as FinderOpts, SuggestionType};
use crate::prelude::*;
use crate::serializer;
use crate::structures::cheat::VariableMap;
use crate::structures::item::Item;
use std::io::Write;
@ -162,9 +162,9 @@ fn gen_lists(tag_rules: &str) -> FilterOpts {
impl<'a> Parser<'a> {
pub fn new(writer: &'a mut dyn Write, is_terminal: bool) -> Self {
let write_fn = if is_terminal {
serializer::write
deser::terminal::write
} else {
serializer::write_raw
deser::raycast::write
};
let filter = match CONFIG.tag_rules() {
@ -256,7 +256,7 @@ impl<'a> Parser<'a> {
// blank
if line.is_empty() {
if !(&item.snippet).is_empty() {
item.snippet.push_str(serializer::LINE_SEPARATOR);
item.snippet.push_str(deser::LINE_SEPARATOR);
}
}
// tag
@ -309,7 +309,7 @@ impl<'a> Parser<'a> {
// snippet
else {
if !(&item.snippet).is_empty() {
item.snippet.push_str(serializer::LINE_SEPARATOR);
item.snippet.push_str(deser::LINE_SEPARATOR);
}
item.snippet.push_str(&line);
}

View file

@ -1,121 +0,0 @@
use crate::common::terminal;
use crate::prelude::*;
use crate::structures::item::Item;
use crossterm::style::{style, Stylize};
use std::cmp::max;
pub fn get_widths() -> (usize, usize, usize) {
let width = terminal::width();
let tag_width_percentage = max(
CONFIG.tag_min_width(),
width * CONFIG.tag_width_percentage() / 100,
);
let comment_width_percentage = max(
CONFIG.comment_min_width(),
width * CONFIG.comment_width_percentage() / 100,
);
let snippet_width_percentage = max(
CONFIG.snippet_min_width(),
width * CONFIG.snippet_width_percentage() / 100,
);
(
usize::from(tag_width_percentage),
usize::from(comment_width_percentage),
usize::from(snippet_width_percentage),
)
}
const NEWLINE_ESCAPE_CHAR: char = '\x15';
const FIELD_SEP_ESCAPE_CHAR: char = '\x16';
pub const LINE_SEPARATOR: &str = " \x15 ";
pub const DELIMITER: &str = r" ";
lazy_static! {
pub static ref NEWLINE_REGEX: Regex = Regex::new(r"\\\s+").expect("Invalid regex");
pub static ref VAR_REGEX: Regex = Regex::new(r"\\?<(\w[\w\d\-_]*)>").expect("Invalid regex");
pub static ref COLUMN_WIDTHS: (usize, usize, usize) = get_widths();
}
pub fn with_new_lines(txt: String) -> String {
txt.replace(LINE_SEPARATOR, "\n")
}
pub fn fix_newlines(txt: &str) -> String {
if txt.contains(NEWLINE_ESCAPE_CHAR) {
(*NEWLINE_REGEX)
.replace_all(txt.replace(LINE_SEPARATOR, " ").as_str(), "")
.to_string()
} else {
txt.to_string()
}
}
fn limit_str(text: &str, length: usize) -> String {
if text.len() > length {
format!("{}", text.chars().take(length - 1).collect::<String>())
} else {
format!("{:width$}", text, width = length)
}
}
pub fn write(item: &Item) -> String {
let (tag_width_percentage, comment_width_percentage, snippet_width_percentage) = *COLUMN_WIDTHS;
format!(
"{tags_short}{delimiter}{comment_short}{delimiter}{snippet_short}{delimiter}{tags}{delimiter}{comment}{delimiter}{snippet}{delimiter}{file_index}{delimiter}\n",
tags_short = style(limit_str(&item.tags, tag_width_percentage)).with(CONFIG.tag_color()),
comment_short = style(limit_str(&item.comment, comment_width_percentage)).with(CONFIG.comment_color()),
snippet_short = style(limit_str(&fix_newlines(&item.snippet), snippet_width_percentage)).with(CONFIG.snippet_color()),
tags = item.tags,
comment = item.comment,
delimiter = DELIMITER,
snippet = &item.snippet.trim_end_matches(LINE_SEPARATOR),
file_index = item.file_index.unwrap_or(0),
)
}
pub fn write_raw(item: &Item) -> String {
format!(
"{hash}{delimiter}{tags}{delimiter}{comment}{delimiter}{icon}{delimiter}{snippet}\n",
hash = item.hash(),
tags = item.tags,
comment = item.comment,
delimiter = FIELD_SEP_ESCAPE_CHAR,
icon = item.icon.clone().unwrap_or_default(),
snippet = &item.snippet.trim_end_matches(LINE_SEPARATOR),
)
}
pub fn raycast_deser(line: &str) -> Result<Item> {
let mut parts = line.split(FIELD_SEP_ESCAPE_CHAR);
let hash: u64 = parts
.next()
.context("no hash")?
.parse()
.context("hash not a u64")?;
let tags = parts.next().context("no tags")?.into();
let comment = parts.next().context("no comment")?.into();
let icon_str = parts.next().context("no icon")?;
let snippet = parts.next().context("no snippet")?.into();
let icon = if icon_str.is_empty() {
None
} else {
Some(icon_str.into())
};
let item = Item {
tags,
comment,
icon,
snippet,
..Default::default()
};
if item.hash() != hash {
dbg!(&item.hash());
dbg!(hash);
Err(anyhow!("Incorrect hash"))
} else {
Ok(item)
}
}

View file

@ -1 +0,0 @@