add tldr and base
This commit is contained in:
2
crates/tldr/.gitignore
vendored
Normal file
2
crates/tldr/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/target
|
||||
/Cargo.lock
|
14
crates/tldr/Cargo.toml
Normal file
14
crates/tldr/Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "tldr"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
util = { path = "../util" }
|
||||
|
||||
eyre.workspace = true
|
||||
clap.workspace = true
|
||||
dirs.workspace = true
|
||||
walkdir.workspace = true
|
86
crates/tldr/src/lib.rs
Normal file
86
crates/tldr/src/lib.rs
Normal file
@@ -0,0 +1,86 @@
|
||||
pub(crate) mod update;
|
||||
|
||||
pub struct Tldr;
|
||||
|
||||
impl Tldr {
|
||||
fn run() -> eyre::Result<()> {
|
||||
let cache_dir =
|
||||
dirs::cache_dir().ok_or(eyre::anyhow!("could not find a valid cache dir"))?;
|
||||
|
||||
let mut tldr_cache_dir = cache_dir.clone();
|
||||
tldr_cache_dir.push("kah-toolkit/tldr/store/");
|
||||
|
||||
if !tldr_cache_dir.exists() {
|
||||
return Err(eyre::anyhow!("you need to run <toolkit tldr update> first"));
|
||||
}
|
||||
|
||||
let mut tldr_pages_path = tldr_cache_dir.clone();
|
||||
tldr_pages_path.push("pages");
|
||||
|
||||
if !tldr_pages_path.exists() {
|
||||
return Err(eyre::anyhow!("you need to run <toolkit tldr update> first"));
|
||||
}
|
||||
|
||||
let mut entries: Vec<String> = Vec::new();
|
||||
for entry in walkdir::WalkDir::new(&tldr_pages_path) {
|
||||
let entry = entry?;
|
||||
|
||||
let path = entry.path().to_path_buf();
|
||||
|
||||
match path.extension() {
|
||||
None => continue,
|
||||
Some(ext) => {
|
||||
if ext != "md" {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let parent_str = path
|
||||
.parent()
|
||||
.ok_or(eyre::anyhow!("could not find parent for file"))?
|
||||
.to_string_lossy();
|
||||
|
||||
let parent = parent_str.split("/").last();
|
||||
let file_name = entry.file_name();
|
||||
|
||||
entries.push(format!(
|
||||
"{}/{}",
|
||||
parent.ok_or(eyre::anyhow!("path contains non ascii characters"))?,
|
||||
file_name
|
||||
.to_str()
|
||||
.ok_or(eyre::anyhow!("path contains non ascii characters"))?,
|
||||
))
|
||||
}
|
||||
|
||||
let paths = entries.join("\n");
|
||||
|
||||
let output = util::shell::run_with_input_and_output(&["fzf"], paths)?;
|
||||
|
||||
let choice = std::str::from_utf8(output.stdout.as_slice())?.trim();
|
||||
|
||||
let mut tldr_choice_path = tldr_pages_path;
|
||||
tldr_choice_path.push(choice);
|
||||
|
||||
let contents = std::fs::read_to_string(tldr_choice_path)?;
|
||||
|
||||
util::shell::run_with_input(&["glow", "-"], contents)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl util::Cmd for Tldr {
|
||||
fn cmd() -> eyre::Result<clap::Command> {
|
||||
let cmd = clap::Command::new("tldr").subcommands([update::Update::cmd()?]);
|
||||
|
||||
Ok(cmd)
|
||||
}
|
||||
|
||||
fn exec(args: &clap::ArgMatches) -> eyre::Result<()> {
|
||||
match args.subcommand() {
|
||||
Some(("update", subcmd)) => update::Update::exec(subcmd),
|
||||
_ => Tldr::run(),
|
||||
}
|
||||
}
|
||||
}
|
32
crates/tldr/src/update.rs
Normal file
32
crates/tldr/src/update.rs
Normal file
@@ -0,0 +1,32 @@
|
||||
pub struct Update;
|
||||
|
||||
impl util::Cmd for Update {
|
||||
fn cmd() -> eyre::Result<clap::Command> {
|
||||
Ok(clap::Command::new("update"))
|
||||
}
|
||||
|
||||
fn exec(_: &clap::ArgMatches) -> eyre::Result<()> {
|
||||
let cache_dir =
|
||||
dirs::cache_dir().ok_or(eyre::anyhow!("could not find a valid cache dir"))?;
|
||||
|
||||
let mut tldr_cache_dir = cache_dir.clone();
|
||||
tldr_cache_dir.push("kah-toolkit/tldr/store/");
|
||||
|
||||
std::fs::remove_dir_all(&tldr_cache_dir)?;
|
||||
std::fs::create_dir_all(&tldr_cache_dir)?;
|
||||
|
||||
util::shell::run(
|
||||
format!(
|
||||
"gh repo clone tldr-pages/tldr {}",
|
||||
&tldr_cache_dir
|
||||
.to_str()
|
||||
.ok_or(eyre::anyhow!("pathstring contains non ascii-characters"))?
|
||||
)
|
||||
.split(" ")
|
||||
.collect::<Vec<&str>>()
|
||||
.as_slice(),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
10
crates/util/Cargo.toml
Normal file
10
crates/util/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "util"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
eyre.workspace = true
|
||||
clap.workspace = true
|
7
crates/util/src/lib.rs
Normal file
7
crates/util/src/lib.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
pub mod shell;
|
||||
|
||||
pub trait Cmd {
|
||||
fn cmd() -> eyre::Result<clap::Command>;
|
||||
fn exec(args: &clap::ArgMatches) -> eyre::Result<()>;
|
||||
}
|
||||
|
123
crates/util/src/shell.rs
Normal file
123
crates/util/src/shell.rs
Normal file
@@ -0,0 +1,123 @@
|
||||
use std::io::Write;
|
||||
|
||||
pub fn run(args: &[&str]) -> eyre::Result<()> {
|
||||
let output = std::process::Command::new(
|
||||
args.first()
|
||||
.ok_or(eyre::anyhow!("could not find first arg"))?,
|
||||
)
|
||||
.args(
|
||||
args.to_vec()
|
||||
.into_iter()
|
||||
.skip(1)
|
||||
.collect::<Vec<&str>>()
|
||||
.as_slice(),
|
||||
)
|
||||
.stdout(std::process::Stdio::inherit())
|
||||
.stderr(std::process::Stdio::inherit())
|
||||
.stdin(std::process::Stdio::inherit())
|
||||
.output();
|
||||
|
||||
match output {
|
||||
Ok(o) => {
|
||||
if o.status.success() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(eyre::anyhow!(
|
||||
"command failed with statuscode: {}",
|
||||
o.status
|
||||
.code()
|
||||
.ok_or(eyre::anyhow!("could not get a status code from process"))?
|
||||
))
|
||||
}
|
||||
}
|
||||
Err(e) => Err(eyre::anyhow!(e)),
|
||||
}
|
||||
}
|
||||
pub fn run_with_input(args: &[&str], input: String) -> eyre::Result<()> {
|
||||
let output = std::process::Command::new(
|
||||
args.first()
|
||||
.ok_or(eyre::anyhow!("could not find first arg"))?,
|
||||
)
|
||||
.args(
|
||||
args.to_vec()
|
||||
.into_iter()
|
||||
.skip(1)
|
||||
.collect::<Vec<&str>>()
|
||||
.as_slice(),
|
||||
)
|
||||
.stdout(std::process::Stdio::inherit())
|
||||
.stderr(std::process::Stdio::inherit())
|
||||
.stdin(std::process::Stdio::piped())
|
||||
.spawn();
|
||||
|
||||
match output {
|
||||
Ok(mut o) => {
|
||||
let stdin = o
|
||||
.stdin
|
||||
.as_mut()
|
||||
.ok_or(eyre::anyhow!("could not acquire stdin"))?;
|
||||
|
||||
stdin.write_all(input.as_bytes())?;
|
||||
drop(stdin);
|
||||
|
||||
let o = o.wait_with_output()?;
|
||||
if o.status.success() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(eyre::anyhow!(
|
||||
"command failed with statuscode: {}",
|
||||
o.status
|
||||
.code()
|
||||
.ok_or(eyre::anyhow!("could not get a status code from process"))?
|
||||
))
|
||||
}
|
||||
}
|
||||
Err(e) => Err(eyre::anyhow!(e)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_with_input_and_output(
|
||||
args: &[&str],
|
||||
input: String,
|
||||
) -> eyre::Result<std::process::Output> {
|
||||
let output = std::process::Command::new(
|
||||
args.first()
|
||||
.ok_or(eyre::anyhow!("could not find first arg"))?,
|
||||
)
|
||||
.args(
|
||||
args.to_vec()
|
||||
.into_iter()
|
||||
.skip(1)
|
||||
.collect::<Vec<&str>>()
|
||||
.as_slice(),
|
||||
)
|
||||
.stdout(std::process::Stdio::piped())
|
||||
.stderr(std::process::Stdio::inherit())
|
||||
.stdin(std::process::Stdio::piped())
|
||||
.spawn();
|
||||
|
||||
match output {
|
||||
Ok(mut o) => {
|
||||
let stdin = o
|
||||
.stdin
|
||||
.as_mut()
|
||||
.ok_or(eyre::anyhow!("could not acquire stdin"))?;
|
||||
|
||||
stdin.write_all(input.as_bytes())?;
|
||||
drop(stdin);
|
||||
|
||||
let o = o.wait_with_output()?;
|
||||
if o.status.success() {
|
||||
Ok(o)
|
||||
} else {
|
||||
Err(eyre::anyhow!(
|
||||
"command failed with statuscode: {}",
|
||||
o.status
|
||||
.code()
|
||||
.ok_or(eyre::anyhow!("could not get a status code from process"))?
|
||||
))
|
||||
}
|
||||
}
|
||||
Err(e) => Err(eyre::anyhow!(e)),
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user