mirror of
https://github.com/denisidoro/navi.git
synced 2026-01-23 02:14:19 +00:00
Feat: Add a flag to bypass confirmations + Add a partial implementation of repo sync (#978)
* Added a start of a functional copy of the .git folder Signed-off-by: alexis-opolka <53085471+alexis-opolka@users.noreply.github.com> * Add the sync file Signed-off-by: alexis-opolka <53085471+alexis-opolka@users.noreply.github.com> * Bump version from beta1 to beta2 + modified sync subcommand entry Signed-off-by: alexis-opolka <53085471+alexis-opolka@users.noreply.github.com> * Made minor modifications Signed-off-by: alexis-opolka <53085471+alexis-opolka@users.noreply.github.com> * Update crossterm to last release Signed-off-by: alexis-opolka <53085471+alexis-opolka@users.noreply.github.com> * Introducing a new internal module - terminal This module should be able to handle specific tasks with the terminal such as hyperlinks or others not handled by crates Signed-off-by: alexis-opolka <53085471+alexis-opolka@users.noreply.github.com> * Add the repo list subcommand Signed-off-by: alexis-opolka <53085471+alexis-opolka@users.noreply.github.com> * cargo fmt + minor improvements Signed-off-by: alexis-opolka <53085471+alexis-opolka@users.noreply.github.com> * Adds a basic sync function + cargo fmt + cargo clippy compliance + minor refactors Signed-off-by: alexis-opolka <53085471+alexis-opolka@users.noreply.github.com> * Add an option for confirmation Signed-off-by: alexis-opolka <53085471+alexis-opolka@users.noreply.github.com> * Updated the status of this branch to a stable state Signed-off-by: alexis-opolka <53085471+alexis-opolka@users.noreply.github.com> --------- Signed-off-by: alexis-opolka <53085471+alexis-opolka@users.noreply.github.com>
This commit is contained in:
parent
138b7e0b00
commit
75eb558198
15 changed files with 456 additions and 44 deletions
88
Cargo.lock
generated
88
Cargo.lock
generated
|
|
@ -156,16 +156,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||
|
||||
[[package]]
|
||||
name = "crossterm"
|
||||
version = "0.28.1"
|
||||
name = "convert_case"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6"
|
||||
checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"crossterm_winapi",
|
||||
"derive_more",
|
||||
"document-features",
|
||||
"mio",
|
||||
"parking_lot",
|
||||
"rustix",
|
||||
"rustix 1.0.7",
|
||||
"signal-hook",
|
||||
"signal-hook-mio",
|
||||
"winapi",
|
||||
|
|
@ -189,6 +200,36 @@ dependencies = [
|
|||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678"
|
||||
dependencies = [
|
||||
"derive_more-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more-impl"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "document-features"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d"
|
||||
dependencies = [
|
||||
"litrs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dunce"
|
||||
version = "1.0.5"
|
||||
|
|
@ -330,6 +371,18 @@ version = "0.4.15"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
|
||||
|
||||
[[package]]
|
||||
name = "litrs"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.12"
|
||||
|
|
@ -375,7 +428,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "navi"
|
||||
version = "2.25.0-beta1"
|
||||
version = "2.25.0-beta2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
|
|
@ -563,7 +616,20 @@ dependencies = [
|
|||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"linux-raw-sys 0.4.15",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys 0.9.4",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
|
|
@ -718,7 +784,7 @@ dependencies = [
|
|||
"fastrand",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"rustix 0.38.43",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
|
|
@ -819,6 +885,12 @@ version = "1.0.14"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.2.0"
|
||||
|
|
@ -877,7 +949,7 @@ dependencies = [
|
|||
"either",
|
||||
"home",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"rustix 0.38.43",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "navi"
|
||||
version = "2.25.0-beta1"
|
||||
version = "2.25.0-beta2"
|
||||
authors = ["Denis Isidoro <denis_isidoro@live.com>", "Alexis Opolka <alexis.opolka@protonmail.com>"]
|
||||
edition = "2021"
|
||||
description = "An interactive cheatsheet tool for the command-line"
|
||||
|
|
@ -24,7 +24,7 @@ regex = { version = "1.7.3", default-features = false, features = [
|
|||
"unicode-perl",
|
||||
] }
|
||||
clap = { version = "4.2.1", features = ["derive", "cargo"] }
|
||||
crossterm = "0.28.0"
|
||||
crossterm = "0.29.0"
|
||||
lazy_static = "1.4.0"
|
||||
etcetera = "0.10.0"
|
||||
walkdir = "2.3.3"
|
||||
|
|
|
|||
|
|
@ -1,10 +1,14 @@
|
|||
use crate::common::git;
|
||||
use crate::filesystem;
|
||||
use crate::filesystem::{
|
||||
all_cheat_files, all_git_files, create_dir, default_cheat_pathbuf, remove_dir, tmp_pathbuf,
|
||||
};
|
||||
use crate::finder::questions::finder_yes_no_question;
|
||||
use crate::finder::structures::{Opts as FinderOpts, SuggestionType};
|
||||
use crate::finder::FinderChoice;
|
||||
use crate::prelude::*;
|
||||
use std::fs;
|
||||
use std::path;
|
||||
use std::path::{MAIN_SEPARATOR, MAIN_SEPARATOR_STR};
|
||||
|
||||
fn ask_if_should_import_all(finder: &FinderChoice) -> Result<bool> {
|
||||
let opts = FinderOpts {
|
||||
|
|
@ -13,37 +17,66 @@ fn ask_if_should_import_all(finder: &FinderChoice) -> Result<bool> {
|
|||
..Default::default()
|
||||
};
|
||||
|
||||
let (response, _) = finder
|
||||
.call(opts, |stdin| {
|
||||
stdin
|
||||
.write_all(b"Yes\nNo")
|
||||
.context("Unable to writer alternatives")?;
|
||||
Ok(())
|
||||
})
|
||||
.context("Unable to get response")?;
|
||||
finder_yes_no_question(finder, opts)
|
||||
}
|
||||
fn ask_folder_present_question(finder: &FinderChoice) -> Result<bool> {
|
||||
let opts = FinderOpts {
|
||||
column: Some(1),
|
||||
header: Some(
|
||||
"It seems this cheatsheet repository has been previously added, do you still want to continue?"
|
||||
.to_string(),
|
||||
),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
Ok(response.to_lowercase().starts_with('y'))
|
||||
finder_yes_no_question(finder, opts)
|
||||
}
|
||||
|
||||
pub fn main(uri: String) -> Result<()> {
|
||||
pub fn main(uri: String, yes_flag: bool) -> Result<()> {
|
||||
let finder = CONFIG.finder();
|
||||
|
||||
let should_import_all = ask_if_should_import_all(&finder).unwrap_or(false);
|
||||
// If the user has set the yes flag, we don't ask a confirmation
|
||||
let should_import_all = if yes_flag {
|
||||
true
|
||||
} else {
|
||||
ask_if_should_import_all(&finder).unwrap_or(false)
|
||||
};
|
||||
|
||||
let (actual_uri, user, repo) = git::meta(uri.as_str());
|
||||
|
||||
let cheat_pathbuf = filesystem::default_cheat_pathbuf()?;
|
||||
let tmp_pathbuf = filesystem::tmp_pathbuf()?;
|
||||
// TODO: Using the default cheat pathbuf will send the downloaded cheatsheets
|
||||
// into the path without taking into account the user-defined paths.
|
||||
let cheat_pathbuf = default_cheat_pathbuf()?;
|
||||
let tmp_pathbuf = tmp_pathbuf()?;
|
||||
let tmp_path_str = &tmp_pathbuf.to_string();
|
||||
let to_folder = {
|
||||
let mut p = cheat_pathbuf;
|
||||
p.push(format!("{user}__{repo}"));
|
||||
p
|
||||
};
|
||||
|
||||
let _ = filesystem::remove_dir(&tmp_pathbuf);
|
||||
filesystem::create_dir(&tmp_pathbuf)?;
|
||||
// Before anything else, we check to see if the folder exists
|
||||
// if it exists -> ask confirmation if we continue
|
||||
if fs::exists(&to_folder)? {
|
||||
// When the yes_flag has been raised => follow through and removes the existing directory
|
||||
// When the yes_flag has not been raised => ask for confirmation
|
||||
if yes_flag || ask_folder_present_question(&finder).unwrap_or(false) {
|
||||
fs::remove_dir_all(&to_folder)?;
|
||||
} else {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
let _ = remove_dir(&tmp_pathbuf);
|
||||
create_dir(&tmp_pathbuf)?;
|
||||
|
||||
eprintln!("Cloning {} into {}...\n", &actual_uri, &tmp_path_str);
|
||||
|
||||
git::shallow_clone(actual_uri.as_str(), tmp_path_str)
|
||||
.with_context(|| format!("Failed to clone `{actual_uri}`"))?;
|
||||
|
||||
let all_files = filesystem::all_cheat_files(&tmp_pathbuf).join("\n");
|
||||
let all_files = all_cheat_files(&tmp_pathbuf).join("\n");
|
||||
let git_files = all_git_files(&tmp_pathbuf).join("\n");
|
||||
|
||||
let opts = FinderOpts {
|
||||
suggestion_type: SuggestionType::MultipleSelections,
|
||||
|
|
@ -67,12 +100,6 @@ pub fn main(uri: String) -> Result<()> {
|
|||
files
|
||||
};
|
||||
|
||||
let to_folder = {
|
||||
let mut p = cheat_pathbuf;
|
||||
p.push(format!("{user}__{repo}"));
|
||||
p
|
||||
};
|
||||
|
||||
for file in files.split('\n') {
|
||||
let from = {
|
||||
let mut p = tmp_pathbuf.clone();
|
||||
|
|
@ -92,7 +119,48 @@ pub fn main(uri: String) -> Result<()> {
|
|||
.with_context(|| format!("Failed to copy `{}` to `{}`", &from.to_string(), &to.to_string()))?;
|
||||
}
|
||||
|
||||
filesystem::remove_dir(&tmp_pathbuf)?;
|
||||
// We are copying the .git folder along with the cheat files
|
||||
// For more details, see: (https://github.com/denisidoro/navi/issues/733)
|
||||
for file in git_files.split('\n') {
|
||||
let filename = format!("{}{}", &tmp_path_str, &file);
|
||||
let from = {
|
||||
let mut p = tmp_pathbuf.clone();
|
||||
p.push(&filename);
|
||||
p
|
||||
};
|
||||
let to = {
|
||||
let mut p = to_folder.clone();
|
||||
p.push(file);
|
||||
p
|
||||
};
|
||||
|
||||
let path_str = &PathBuf::clone(&to).to_string();
|
||||
let local_collection = &path_str.split(MAIN_SEPARATOR).collect::<Vec<&str>>();
|
||||
let local_to_folder = format!(
|
||||
"{}{}",
|
||||
&to_folder.to_string(),
|
||||
local_collection[0..&local_collection.len() - 1].join(MAIN_SEPARATOR_STR)
|
||||
);
|
||||
|
||||
let complete_local_path = format!(
|
||||
"{}{}",
|
||||
&to_folder.clone().to_str().unwrap(),
|
||||
&to.clone().to_str().unwrap()
|
||||
);
|
||||
|
||||
eprintln!("=> {}", &complete_local_path);
|
||||
|
||||
fs::create_dir_all(&local_to_folder).unwrap_or(());
|
||||
fs::copy(&from, &complete_local_path).with_context(|| {
|
||||
format!(
|
||||
"Failed to copy `{}` to `{}`",
|
||||
&from.to_string(),
|
||||
&complete_local_path
|
||||
)
|
||||
})?;
|
||||
}
|
||||
|
||||
remove_dir(&tmp_pathbuf)?;
|
||||
|
||||
eprintln!(
|
||||
"The following .cheat files were imported successfully:\n{}\n\nThey are now located at {}",
|
||||
|
|
|
|||
34
src/commands/repo/list.rs
Normal file
34
src/commands/repo/list.rs
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
use crate::filesystem::local_cheatsheet_repositories;
|
||||
use crate::libs::terminal::hyperlink;
|
||||
|
||||
pub fn main() {
|
||||
let (cheats_repos, _) = local_cheatsheet_repositories();
|
||||
|
||||
// Now that we have our list of cheatsheet repositories, we loop through them
|
||||
// Two behaviours:
|
||||
// We do have entries -> We show them
|
||||
// We do not have entries -> We put a message for the user to add one
|
||||
if cheats_repos.is_empty() {
|
||||
eprintln!("Uh Oh! It seems you haven't downloaded a cheatsheet repository yet.");
|
||||
eprintln!("What you can do: \n\n- `navi repo add` to add a cheatsheet repository\n- `navi repo browse` to browse recommended cheatsheet repositories");
|
||||
|
||||
// We quit this function
|
||||
return;
|
||||
}
|
||||
|
||||
// The list shouldn't be empty
|
||||
eprintln!("You have locally available the following cheatsheet repositories: \n");
|
||||
|
||||
for cheat_repo in cheats_repos {
|
||||
let content = if cheat_repo.starts_with("https://") {
|
||||
// If the URL is using the HTTPS scheme, we use a hyperlink
|
||||
// instead of printing its raw value.
|
||||
|
||||
hyperlink::new(&cheat_repo, &cheat_repo)
|
||||
} else {
|
||||
cheat_repo.to_owned()
|
||||
};
|
||||
|
||||
eprintln!("- {}", content);
|
||||
}
|
||||
}
|
||||
|
|
@ -4,16 +4,28 @@ use clap::{Args, Subcommand};
|
|||
|
||||
pub mod add;
|
||||
pub mod browse;
|
||||
mod list;
|
||||
mod sync;
|
||||
|
||||
#[derive(Debug, Clone, Subcommand)]
|
||||
pub enum RepoCommand {
|
||||
/// Browses for featured cheatsheet repos
|
||||
Browse,
|
||||
/// Imports cheatsheets from a repo
|
||||
Add {
|
||||
/// A URI to a git repository containing .cheat files ("user/repo" will download cheats from github.com/user/repo)
|
||||
uri: String,
|
||||
/// Assumes yes for all confirmations
|
||||
#[clap(short = 'y', long = "yes")]
|
||||
yes_flag: bool,
|
||||
},
|
||||
/// Browses for featured cheatsheet repos
|
||||
Browse,
|
||||
/// Synchronize either all cheatsheet repositories or a given one.
|
||||
Sync {
|
||||
/// The name of the cheatsheet repository to sync.
|
||||
name: Option<String>,
|
||||
},
|
||||
/// List all downloaded repositories
|
||||
List,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Args)]
|
||||
|
|
@ -25,17 +37,29 @@ pub struct Input {
|
|||
impl Runnable for Input {
|
||||
fn run(&self) -> Result<()> {
|
||||
match &self.cmd {
|
||||
RepoCommand::Add { uri } => {
|
||||
add::main(uri.clone())
|
||||
RepoCommand::Add { uri, yes_flag } => {
|
||||
add::main(uri.clone(), *yes_flag)
|
||||
.with_context(|| format!("Failed to import cheatsheets from `{uri}`"))?;
|
||||
|
||||
commands::core::main()
|
||||
}
|
||||
RepoCommand::Browse => {
|
||||
let repo = browse::main().context("Failed to browse featured cheatsheets")?;
|
||||
add::main(repo.clone())
|
||||
add::main(repo.clone(), false)
|
||||
.with_context(|| format!("Failed to import cheatsheets from `{repo}`"))?;
|
||||
|
||||
commands::core::main()
|
||||
}
|
||||
RepoCommand::Sync { name } => {
|
||||
sync::main(name.clone())
|
||||
// TODO: Remove the debug extension later on
|
||||
.with_context(|| format!("Failed to synchronize cheatsheets from `{:?}`", name))
|
||||
}
|
||||
RepoCommand::List => {
|
||||
list::main();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
35
src/commands/repo/sync.rs
Normal file
35
src/commands/repo/sync.rs
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
use crate::common::git;
|
||||
use crate::filesystem::local_cheatsheet_repositories;
|
||||
use crate::prelude::*;
|
||||
|
||||
pub fn main(name: Option<String>) -> Result<()> {
|
||||
let (cheats_repo_uris, cheats_repo_paths) = local_cheatsheet_repositories();
|
||||
|
||||
if name.clone().is_some() {
|
||||
let name = name.clone().unwrap();
|
||||
|
||||
// We have been given a repository uri to check
|
||||
if cheats_repo_uris.contains(&name) {
|
||||
let folder_index = cheats_repo_uris.iter().position(|r| r == &name).unwrap();
|
||||
let repo_path = cheats_repo_paths[folder_index].clone();
|
||||
|
||||
git::pull(&repo_path)?;
|
||||
} else {
|
||||
eprintln!("I don't find {} locally, are you sure you downloaded it?", &name);
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// We haven't been given a name -> We synchronize every cheatsheet repository we've found
|
||||
for cheat_repo in cheats_repo_paths {
|
||||
eprintln!("Pulling the latest version of {}", cheat_repo);
|
||||
|
||||
git::pull(&cheat_repo)?;
|
||||
}
|
||||
|
||||
// TODO: Sanitize the cheatsheet folder of any file that is not a cheat file
|
||||
// Ref: https://github.com/denisidoro/navi/issues/733
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -27,6 +27,55 @@ pub fn meta(uri: &str) -> (String, String, String) {
|
|||
(actual_uri, user.to_string(), repo)
|
||||
}
|
||||
|
||||
/// Retrieves the remote URI of a git repository
|
||||
/// Works best with a repository containing only one remote.
|
||||
pub fn get_remote(uri: &str) -> Result<String> {
|
||||
// We consider the repository having only one remote
|
||||
// In case of multiple occurrences, we return the first one and discard the others
|
||||
|
||||
let git_path = format!("{}/.git/", &uri);
|
||||
let mut remotes_uri: Vec<String> = Vec::new();
|
||||
|
||||
if std::fs::exists(&git_path)? {
|
||||
// If the path exists, retrieve the remotes
|
||||
let remotes = Command::new("git")
|
||||
.current_dir(&git_path)
|
||||
.args(["remote"])
|
||||
.output()
|
||||
.context("Unable to git remote")?;
|
||||
|
||||
// This is the name of the remote, not its URI
|
||||
let current_remote = String::from_utf8_lossy(&remotes.stdout).trim().to_string();
|
||||
|
||||
let remote_uri = Command::new("git")
|
||||
.current_dir(&git_path)
|
||||
.args(["config", format!("remote.{}.url", current_remote).as_str()])
|
||||
.output()
|
||||
.context(format!(
|
||||
"Unable to git config remote <remote>.url for {}",
|
||||
¤t_remote
|
||||
))?;
|
||||
|
||||
// This is the URI of the remote
|
||||
let current_remote_uri = String::from_utf8_lossy(&remote_uri.stdout).trim().to_string();
|
||||
|
||||
remotes_uri.push(current_remote_uri);
|
||||
}
|
||||
|
||||
Ok(remotes_uri[0].clone())
|
||||
}
|
||||
|
||||
/// Pulls the latest version of a git repository
|
||||
pub fn pull(uri: &str) -> Result<()> {
|
||||
Command::new("git")
|
||||
.current_dir(uri)
|
||||
.args(["pull", "origin"])
|
||||
.spawn()?
|
||||
.wait()
|
||||
.expect("Unable to git pull");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
|||
|
|
@ -26,3 +26,11 @@ impl EnvConfig {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A default implementation for EnvConfig
|
||||
/// to satisfy cargo clippy.
|
||||
impl Default for EnvConfig {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,6 +103,10 @@ impl Config {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn yaml(&self) -> &YamlConfig {
|
||||
&self.yaml
|
||||
}
|
||||
|
||||
pub fn finder(&self) -> FinderChoice {
|
||||
self.clap
|
||||
.finder
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ use regex::Regex;
|
|||
use std::cell::RefCell;
|
||||
use std::path::MAIN_SEPARATOR;
|
||||
|
||||
use crate::common::git;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
/// Multiple paths are joint by a platform-specific separator.
|
||||
|
|
@ -29,6 +30,32 @@ pub fn all_cheat_files(path: &Path) -> Vec<String> {
|
|||
.collect::<Vec<String>>()
|
||||
}
|
||||
|
||||
pub fn all_git_files(path: &Path) -> Vec<String> {
|
||||
let mut path_str = path.to_str().unwrap().to_owned();
|
||||
if path_str.ends_with("/") {
|
||||
// We're removing the trailing '/' at the end, if it exists
|
||||
path_str.push('/');
|
||||
}
|
||||
|
||||
WalkDir::new(path)
|
||||
.follow_links(true)
|
||||
.into_iter()
|
||||
.filter_map(|e| e.ok())
|
||||
.map(|e| {
|
||||
return if e.path().is_file() {
|
||||
e.path()
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.replace(path_str.as_str(), "")
|
||||
.to_string()
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
})
|
||||
.filter(|e| e.contains("/.git/"))
|
||||
.collect::<Vec<String>>()
|
||||
}
|
||||
|
||||
fn paths_from_path_param(env_var: &str) -> impl Iterator<Item = &str> {
|
||||
env_var.split(JOIN_SEPARATOR).filter(|folder| folder != &"")
|
||||
}
|
||||
|
|
@ -88,6 +115,16 @@ pub fn cheat_paths(path: Option<String>) -> Result<String> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the cheats path defined at run time
|
||||
pub fn running_cheats_path() -> String {
|
||||
CONFIG.path().unwrap_or_else(|| {
|
||||
// if we don't have a path, use the default value
|
||||
let _cheats = default_cheat_pathbuf().unwrap();
|
||||
|
||||
_cheats.to_string()
|
||||
})
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Here are other functions, unrelated to CLI commands (or at least not directly related)
|
||||
|
|
@ -124,6 +161,54 @@ fn get_config_dir_by_platform() -> Result<PathBuf> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Goes through the cheats path(s) defined within the configuration
|
||||
/// and sends back any cheatsheet repository remote URI it finds.
|
||||
pub fn local_cheatsheet_repositories() -> (Vec<String>, Vec<String>) {
|
||||
let mut cheats_repos_uri: Vec<String> = Vec::new();
|
||||
let mut cheats_repos_paths: Vec<String> = Vec::new();
|
||||
let cheats = running_cheats_path();
|
||||
|
||||
// We're checking each given paths possible
|
||||
for cheat_path in cheats.split(':') {
|
||||
// If the path doesn't exist, continue to the next one
|
||||
if !std::fs::exists(cheat_path).unwrap() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let curr_dir = std::fs::read_dir(cheat_path).unwrap();
|
||||
|
||||
// We're checking subfolders -> they should contain at least one .cheat files
|
||||
for entry in curr_dir {
|
||||
let entry = entry.unwrap();
|
||||
|
||||
if entry.file_type().unwrap().is_dir() {
|
||||
// If the directory doesn't have at least one cheat file -> ignore it and continue
|
||||
if all_cheat_files(&entry.path()).is_empty() {
|
||||
continue;
|
||||
};
|
||||
|
||||
// If the directory have at least one cheat file -> add it to the list
|
||||
// Note: for the list, we are registering the git remote name and not the
|
||||
// folder name since we modify it internally.
|
||||
let git_path = format!("{}/{}", &entry.path().display(), ".git");
|
||||
let folder_path = entry.path().display().to_string();
|
||||
|
||||
if std::fs::exists(&git_path).unwrap() {
|
||||
let remote_uri = git::get_remote(&entry.path().to_string()).unwrap();
|
||||
|
||||
cheats_repos_uri.push(remote_uri);
|
||||
} else {
|
||||
cheats_repos_uri.push(folder_path.clone());
|
||||
}
|
||||
|
||||
cheats_repos_paths.push(folder_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(cheats_repos_uri, cheats_repos_paths)
|
||||
}
|
||||
|
||||
pub fn tmp_pathbuf() -> Result<PathBuf> {
|
||||
let mut root = default_cheat_pathbuf()?;
|
||||
root.push("tmp");
|
||||
|
|
|
|||
|
|
@ -1,20 +1,22 @@
|
|||
use crate::deser;
|
||||
use crate::prelude::*;
|
||||
use clap::ValueEnum;
|
||||
pub use post::process;
|
||||
use std::io::Write;
|
||||
use std::process::{self, Output};
|
||||
use std::process::{Command, Stdio};
|
||||
pub mod structures;
|
||||
use clap::ValueEnum;
|
||||
pub use post::process;
|
||||
use structures::Opts;
|
||||
use structures::SuggestionType;
|
||||
|
||||
pub mod questions;
|
||||
pub mod structures;
|
||||
|
||||
mod post;
|
||||
|
||||
const MIN_FZF_VERSION_MAJOR: u32 = 0;
|
||||
const MIN_FZF_VERSION_MINOR: u32 = 23;
|
||||
const MIN_FZF_VERSION_PATCH: u32 = 1;
|
||||
|
||||
mod post;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Deserialize, ValueEnum)]
|
||||
pub enum FinderChoice {
|
||||
Fzf,
|
||||
|
|
|
|||
16
src/finder/questions.rs
Normal file
16
src/finder/questions.rs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
use crate::finder::structures::Opts;
|
||||
use crate::finder::FinderChoice;
|
||||
use crate::prelude::*;
|
||||
|
||||
pub fn finder_yes_no_question(finder: &FinderChoice, opts: Opts) -> anyhow::Result<bool> {
|
||||
let (response, _) = finder
|
||||
.call(opts, |stdin| {
|
||||
stdin
|
||||
.write_all(b"Yes\nNo")
|
||||
.context("Unable to writer alternatives")?;
|
||||
Ok(())
|
||||
})
|
||||
.context("Unable to get response")?;
|
||||
|
||||
Ok(response.to_lowercase().starts_with('y'))
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@ mod welcome;
|
|||
|
||||
mod libs {
|
||||
pub mod dns_common;
|
||||
pub mod terminal;
|
||||
}
|
||||
|
||||
pub use {commands::handle, filesystem::default_config_pathbuf};
|
||||
|
|
|
|||
12
src/libs/terminal/hyperlink.rs
Normal file
12
src/libs/terminal/hyperlink.rs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
/// Takes a URI and a label, returns a UNIX-compliant hyperlink
|
||||
/// See the definition of this function for more details.
|
||||
pub fn new(uri: &String, label: &String) -> String {
|
||||
// This is a temporary way of creating a hyperlink until we find a suitable crate
|
||||
// to handle this kind of use cases -> A maintained crated specialized in this use case will be
|
||||
// safer to use than inserting ourselves the escape sequences.
|
||||
// For more details, see:
|
||||
// - Terminal hyperlink -> https://askubuntu.com/questions/1391071/creating-a-hyperlink-from-command-line-output-on-a-terminal
|
||||
// - Rust Hexadecimal escape characters -> https://stackoverflow.com/a/33139393/13025136
|
||||
|
||||
format!("\x1b]8;;{}\x1b\\{}\x1b]8;;\x1b\\\n", uri, label)
|
||||
}
|
||||
2
src/libs/terminal/mod.rs
Normal file
2
src/libs/terminal/mod.rs
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
/// This module exposes functions for use with the terminal.
|
||||
pub mod hyperlink;
|
||||
Loading…
Add table
Add a link
Reference in a new issue