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 crate::prelude::*;
use std::process::{self, Command, Stdio}; use std::process::{Command, Stdio};
lazy_static! { lazy_static! {
pub static ref VAR_TLDR_REGEX: Regex = Regex::new(r"\{\{(.*?)\}\}").expect("Invalid regex"); 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::clipboard;
use crate::common::fs; use crate::common::fs;
use crate::common::shell; use crate::common::shell;
use crate::common::shell::ShellSpawnError; use crate::common::shell::ShellSpawnError;
use crate::config::Action; use crate::config::Action;
use crate::deser;
use crate::env_var; use crate::env_var;
use crate::finder::structures::{Opts as FinderOpts, SuggestionType}; use crate::finder::structures::{Opts as FinderOpts, SuggestionType};
use crate::prelude::*; use crate::prelude::*;
use crate::serializer;
use crate::structures::cheat::{Suggestion, VariableMap}; use crate::structures::cheat::{Suggestion, VariableMap};
use crate::structures::item::Item;
use shell::EOF; use shell::EOF;
use std::process::Stdio; 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> { fn replace_variables_from_snippet(snippet: &str, tags: &str, variables: VariableMap) -> Result<String> {
let mut interpolated_snippet = String::from(snippet); let mut interpolated_snippet = String::from(snippet);
let variables_found: Vec<&str> = serializer::VAR_REGEX let variables_found: Vec<&str> = deser::VAR_REGEX.find_iter(snippet).map(|m| m.as_str()).collect();
.find_iter(snippet)
.map(|m| m.as_str())
.collect();
let variable_count = unique_result_count(&variables_found); let variable_count = unique_result_count(&variables_found);
for bracketed_variable_name in variables_found { for bracketed_variable_name in variables_found {
@ -187,11 +184,20 @@ pub fn with_absolute_path(snippet: String) -> String {
} }
pub fn act( pub fn act(
extractions: Result<extractor::Output>, extractions: Result<(&str, Item)>,
files: Vec<String>, files: Vec<String>,
variables: Option<VariableMap>, variables: Option<VariableMap>,
) -> Result<()> { ) -> Result<()> {
let (key, tags, comment, snippet, file_index) = extractions.unwrap(); let (
key,
Item {
tags,
comment,
snippet,
file_index,
..
},
) = extractions.unwrap();
if key == "ctrl-o" { if key == "ctrl-o" {
edit::edit_file(Path::new(&files[file_index.expect("No files found")])) edit::edit_file(Path::new(&files[file_index.expect("No files found")]))
@ -205,13 +211,13 @@ pub fn act(
let interpolated_snippet = { let interpolated_snippet = {
let mut s = replace_variables_from_snippet( let mut s = replace_variables_from_snippet(
snippet, &snippet,
tags, &tags,
variables.expect("No variables received from finder"), variables.expect("No variables received from finder"),
) )
.context("Failed to replace variables from snippet")?; .context("Failed to replace variables from snippet")?;
s = with_absolute_path(s); s = with_absolute_path(s);
s = serializer::with_new_lines(s); s = deser::with_new_lines(s);
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 actor;
mod extractor;
use crate::clients::cheatsh; use crate::clients::{cheatsh, tldr};
use crate::config::Source; use crate::config::Source;
use crate::deser;
use crate::filesystem; use crate::filesystem;
use crate::finder::structures::Opts as FinderOpts; use crate::finder::structures::Opts as FinderOpts;
use crate::parser::Parser; use crate::parser::Parser;
@ -32,7 +32,7 @@ pub fn init(fetcher: Box<dyn Fetcher>) -> Result<()> {
}) })
.context("Failed getting selection and variables from finder")?; .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() { if extractions.is_err() {
return init(fetcher); return init(fetcher);
@ -51,7 +51,7 @@ pub fn get_fetcher() -> Result<Box<dyn Fetcher>> {
Ok(fetcher) Ok(fetcher)
} }
Source::Tldr(query) => { Source::Tldr(query) => {
let lines = cheatsh::call(&query)?; let lines = tldr::call(&query)?;
let fetcher = Box::new(StaticFetcher::new(lines)); let fetcher = Box::new(StaticFetcher::new(lines));
Ok(fetcher) Ok(fetcher)
} }

View file

@ -8,7 +8,7 @@ use crate::prelude::*;
use clap::Args; use clap::Args;
use clap::Parser; use clap::Parser;
const FUNC_POSSIBLE_VALUES: &[&str] = &[ const POSSIBLE_VALUES: &[&str] = &[
"url::open", "url::open",
"welcome", "welcome",
"widget::last_command", "widget::last_command",
@ -43,7 +43,7 @@ pub enum Func {
#[derive(Debug, Clone, Args)] #[derive(Debug, Clone, Args)]
pub struct Input { pub struct Input {
/// Function name (example: "url::open") /// 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, pub func: Func,
/// List of arguments (example: "https://google.com") /// List of arguments (example: "https://google.com")
pub args: Vec<String>, 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::filesystem;
use crate::prelude::*; 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 { impl FromStr for Info {
type Err = &'static str; type Err = &'static str;
@ -21,7 +21,7 @@ impl FromStr for Info {
#[derive(Debug, Clone, Args)] #[derive(Debug, Clone, Args)]
pub struct Input { pub struct Input {
#[clap(possible_values = INFO_POSSIBLE_VALUES, ignore_case = true)] #[clap(possible_values = POSSIBLE_VALUES, ignore_case = true)]
pub info: Info, pub info: Info,
} }
@ -46,3 +46,10 @@ impl Runnable for Input {
Ok(()) 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::prelude::*;
use crate::serializer;
use clap::Args; use clap::Args;
use crossterm::style::{style, Stylize}; use crossterm::style::{style, Stylize};
use std::process; use std::process;
@ -14,7 +14,7 @@ pub struct Input {
} }
fn extract_elements(argstr: &str) -> Result<(&str, &str, &str)> { 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 tags = parts.next().context("No `tags` element provided.")?;
let comment = parts.next().context("No `comment` element provided.")?; let comment = parts.next().context("No `comment` element provided.")?;
let snippet = parts.next().context("No `snippet` element provided.")?; let snippet = parts.next().context("No `snippet` element provided.")?;
@ -31,7 +31,7 @@ impl Runnable for Input {
"{comment} {tags} \n{snippet}", "{comment} {tags} \n{snippet}",
comment = style(comment).with(CONFIG.comment_color()), comment = style(comment).with(CONFIG.comment_color()),
tags = style(format!("[{}]", tags)).with(CONFIG.tag_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) process::exit(0)

View file

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

View file

@ -3,7 +3,7 @@ use clap::Args;
use crate::common::shell::Shell; use crate::common::shell::Shell;
use crate::prelude::*; use crate::prelude::*;
const WIDGET_POSSIBLE_VALUES: &[&str] = &["bash", "zsh", "fish", "elvish"]; const POSSIBLE_VALUES: &[&str] = &["bash", "zsh", "fish", "elvish"];
impl FromStr for Shell { impl FromStr for Shell {
type Err = &'static str; type Err = &'static str;
@ -21,7 +21,7 @@ impl FromStr for Shell {
#[derive(Debug, Clone, Args)] #[derive(Debug, Clone, Args)]
pub struct Input { 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, pub shell: Shell,
} }
@ -41,3 +41,10 @@ impl Runnable for Input {
Ok(()) 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::common::shell::{self, ShellSpawnError};
use crate::finder::structures::Opts as FinderOpts; use crate::finder::structures::Opts as FinderOpts;
use crate::parser::Parser; use crate::parser::Parser;
use crate::{prelude::*, serializer}; use crate::{deser, prelude::*};
use std::io::{self, Write}; use std::io::{self, Write};
pub fn main() -> Result<()> { pub fn main() -> Result<()> {
let config = &CONFIG; let _config = &CONFIG;
let _opts = FinderOpts::snippet_default(); let _opts = FinderOpts::snippet_default();
let fetcher = get_fetcher()?; let fetcher = get_fetcher()?;
@ -22,7 +22,7 @@ pub fn main() -> Result<()> {
let variables = parser.variables; let variables = parser.variables;
let item_str = String::from_utf8(buf)?; let item_str = String::from_utf8(buf)?;
let item = serializer::raycast_deser(&item_str)?; let item = deser::raycast::read(&item_str)?;
dbg!(&item); dbg!(&item);
let x = variables.get_suggestion(&item.tags, "local_branch").expect("foo"); let x = variables.get_suggestion(&item.tags, "local_branch").expect("foo");
@ -49,7 +49,7 @@ pub fn main() -> Result<()> {
} }
pub fn _main0() -> Result<()> { pub fn _main0() -> Result<()> {
let config = &CONFIG; let _config = &CONFIG;
let fetcher = get_fetcher()?; let fetcher = get_fetcher()?;

View file

@ -1,10 +1,8 @@
use crate::commands; use crate::commands;
use crate::finder::FinderChoice; use crate::finder::{self, FinderChoice};
use crate::prelude::*; use crate::prelude::*;
use clap::{crate_version, AppSettings, Parser, Subcommand}; use clap::{crate_version, AppSettings, Parser, Subcommand};
const FINDER_POSSIBLE_VALUES: &[&str] = &["fzf", "skim"];
#[derive(Debug, Parser)] #[derive(Debug, Parser)]
#[clap(after_help = "\x1b[0;33mMORE INFO:\x1b[0;0m #[clap(after_help = "\x1b[0;33mMORE INFO:\x1b[0;0m
Please refer to \x1b[0;32mhttps://github.com/denisidoro/navi\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>, pub fzf_overrides_var: Option<String>,
/// Finder application to use /// 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>, pub finder: Option<FinderChoice>,
#[clap(subcommand)] #[clap(subcommand)]
@ -127,38 +125,3 @@ pub enum Action {
Print, Print,
Execute, 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::prelude::*;
use crate::serializer;
use std::io::Write; use std::io::Write;
use std::process::{self, Output}; use std::process::{self, Output};
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
@ -17,6 +16,8 @@ pub enum FinderChoice {
Skim, Skim,
} }
pub const POSSIBLE_VALUES: &[&str] = &["fzf", "skim"];
impl FromStr for FinderChoice { impl FromStr for FinderChoice {
type Err = &'static str; type Err = &'static str;
@ -78,7 +79,7 @@ impl FinderChoice {
"--with-nth", "--with-nth",
"1,2,3", "1,2,3",
"--delimiter", "--delimiter",
serializer::DELIMITER.to_string().as_str(), deser::terminal::DELIMITER.to_string().as_str(),
"--ansi", "--ansi",
"--bind", "--bind",
format!("ctrl-j:down,ctrl-k:up{}", bindings).as_str(), format!("ctrl-j:down,ctrl-k:up{}", bindings).as_str(),
@ -189,3 +190,10 @@ impl FinderChoice {
Ok((output, return_value)) 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 commands;
mod common; mod common;
mod config; mod config;
mod deser;
mod env_var; mod env_var;
mod filesystem; mod filesystem;
mod finder; mod finder;
mod parser; mod parser;
mod prelude; mod prelude;
mod serializer;
mod structures; mod structures;
mod terminal;
mod welcome; mod welcome;
pub use commands::handle; pub use commands::handle;

View file

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