From bc3686d8af2c1db962fc4d04d72e8452aaff4176 Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Thu, 25 Apr 2024 12:40:14 +0200 Subject: [PATCH] lib: Switch to radicle-cli and remove duplicate code --- Cargo.lock | 421 +++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 3 +- bin/main.rs | 3 +- bin/terminal.rs | 143 --------------- bin/terminal/args.rs | 134 -------------- bin/terminal/io.rs | 64 ------- 6 files changed, 412 insertions(+), 356 deletions(-) delete mode 100644 bin/terminal.rs delete mode 100644 bin/terminal/args.rs delete mode 100644 bin/terminal/io.rs diff --git a/Cargo.lock b/Cargo.lock index d3ed0190..1286f171 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -64,6 +64,15 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "allocator-api2" version = "0.2.16" @@ -129,6 +138,35 @@ dependencies = [ "libc", ] +[[package]] +name = "anstream" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + [[package]] name = "anstyle-query" version = "1.0.2" @@ -138,6 +176,16 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + [[package]] name = "anyhow" version = "1.0.72" @@ -302,9 +350,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.81" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c6b2562119bf28c3439f7f02db99faf0aa1a8cdfe5772a2ee155d32227239f0" +checksum = "17f6e324229dc011159fcc089755d1e2e216a90d43a7dea6853ca740b84f35e7" dependencies = [ "jobserver", "libc", @@ -352,6 +400,22 @@ dependencies = [ "inout", ] +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "colored" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" +dependencies = [ + "lazy_static", + "windows-sys 0.48.0", +] + [[package]] name = "compact_str" version = "0.7.1" @@ -529,6 +593,12 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + [[package]] name = "digest" version = "0.10.7" @@ -633,6 +703,18 @@ dependencies = [ "libc", ] +[[package]] +name = "escargot" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f474c6844cbd04e783d0f25757583db4f491770ca618bedf2fb01815fc79939" +dependencies = [ + "log", + "once_cell", + "serde", + "serde_json", +] + [[package]] name = "fastrand" version = "2.0.0" @@ -673,9 +755,9 @@ dependencies = [ [[package]] name = "form_urlencoded" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] @@ -845,9 +927,9 @@ dependencies = [ [[package]] name = "idna" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -1126,6 +1208,12 @@ dependencies = [ "serde", ] +[[package]] +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + [[package]] name = "num-bigint-dig" version = "0.8.4" @@ -1313,9 +1401,9 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "phf" @@ -1403,6 +1491,16 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "pretty_assertions" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" +dependencies = [ + "diff", + "yansi", +] + [[package]] name = "primeorder" version = "0.13.6" @@ -1445,6 +1543,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "qcheck" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b439bd4242da51d62d18c95e6a6add749346756b0d1a587dfd0cc22fa6b5f3f0" +dependencies = [ + "rand", +] + [[package]] name = "quote" version = "1.0.35" @@ -1462,6 +1569,8 @@ checksum = "030f82578c59d0f1d4c5ceaede44d5f8b9c8193628e55d28025d7b540057c50d" dependencies = [ "amplify", "base64 0.21.7", + "chrono", + "colored", "crossbeam-channel", "cyphernet", "fastrand", @@ -1472,6 +1581,7 @@ dependencies = [ "multibase", "nonempty 0.9.0", "once_cell", + "qcheck", "radicle-cob", "radicle-crypto", "radicle-git-ext", @@ -1485,6 +1595,63 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "radicle-cli" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89fbe33c5d79e748716bcce09df5220622ee205209a3dd81fe840a631df7a59b" +dependencies = [ + "anyhow", + "chrono", + "git-ref-format", + "lexopt", + "localtime", + "log", + "nonempty 0.9.0", + "radicle", + "radicle-cli-test", + "radicle-cob", + "radicle-crypto", + "radicle-git-ext", + "radicle-surf 0.19.0", + "radicle-term", + "serde", + "serde_json", + "shlex", + "tempfile", + "thiserror", + "timeago", + "tree-sitter", + "tree-sitter-bash", + "tree-sitter-c", + "tree-sitter-css", + "tree-sitter-go", + "tree-sitter-highlight", + "tree-sitter-html", + "tree-sitter-json", + "tree-sitter-md", + "tree-sitter-python", + "tree-sitter-rust", + "tree-sitter-toml", + "tree-sitter-typescript", + "zeroize", +] + +[[package]] +name = "radicle-cli-test" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed1637aa46c99b5296103edf1ef30aead1b6281bd9206aee0bb0e9cc0c0f7707" +dependencies = [ + "escargot", + "log", + "pretty_assertions", + "radicle", + "shlex", + "snapbox", + "thiserror", +] + [[package]] name = "radicle-cob" version = "0.9.0" @@ -1513,7 +1680,9 @@ dependencies = [ "amplify", "cyphernet", "ec25519", + "fastrand", "multibase", + "qcheck", "radicle-git-ext", "radicle-ssh", "serde", @@ -1582,6 +1751,25 @@ dependencies = [ "thiserror", ] +[[package]] +name = "radicle-surf" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fade014fab67a7bdb816e4cf821611af5f0bc55c6eb1018561027ba6ba3b6b28" +dependencies = [ + "anyhow", + "base64 0.13.1", + "flate2", + "git2", + "log", + "nonempty 0.5.0", + "radicle-git-ext", + "radicle-std-ext", + "tar", + "thiserror", + "url", +] + [[package]] name = "radicle-term" version = "0.9.0" @@ -1612,7 +1800,8 @@ dependencies = [ "log", "nom", "radicle", - "radicle-surf", + "radicle-cli", + "radicle-surf 0.18.0", "radicle-term", "ratatui", "serde", @@ -1716,6 +1905,35 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb" +[[package]] +name = "regex" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12de2eff854e5fa4b1295edd650e227e9d8fb0c9e90b12e7f36d6a6811791a29" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49530408a136e16e5b486e883fbb6ba058e8e4e8ae6621a77b048b314336e629" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" + [[package]] name = "rfc6979" version = "0.4.0" @@ -1851,6 +2069,12 @@ dependencies = [ "keccak", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signal-hook" version = "0.3.17" @@ -1886,6 +2110,12 @@ dependencies = [ "rand_core", ] +[[package]] +name = "similar" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640" + [[package]] name = "simple-logging" version = "2.0.2" @@ -1921,6 +2151,28 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043" +[[package]] +name = "snapbox" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b831b6e80fbcd2889efa75b185d24005f85981431495f995292b25836519d84" +dependencies = [ + "anstream", + "anstyle", + "normalize-line-endings", + "similar", + "snapbox-macros", +] + +[[package]] +name = "snapbox-macros" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1c4b838b05d15ab22754068cb73500b2f3b07bf09d310e15b27f88160f1de40" +dependencies = [ + "anstream", +] + [[package]] name = "socket2" version = "0.5.5" @@ -2236,9 +2488,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "timeago" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5082dc942361cdfb74eab98bf995762d6015e5bb3a20bf7c5c71213778b4fcb4" +checksum = "a1710e589de0a76aaf295cd47a6699f6405737dbfd3cf2b75c92d000b548d0e6" dependencies = [ "chrono", "isolang", @@ -2300,6 +2552,137 @@ dependencies = [ "tokio", ] +[[package]] +name = "tree-sitter" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e747b1f9b7b931ed39a548c1fae149101497de3c1fc8d9e18c62c1a66c683d3d" +dependencies = [ + "cc", + "regex", +] + +[[package]] +name = "tree-sitter-bash" +version = "0.20.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57da2032c37eb2ce29fd18df7d3b94355fec8d6d854d8f80934955df542b5906" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-c" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bbd5f3d8658c08581f8f2adac6c391c2e9fa00fe9246bf6c5f52213b9cc6b72" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-css" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3306ddefa1d2681adda2613d11974ffabfbeb215e23235da6c862f3493a04fd" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-go" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad6d11f19441b961af2fda7f12f5d0dac325f6d6de83836a1d3750018cc5114" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-highlight" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "042342584c5a7a0b833d9fc4e2bdab3f9868ddc6c4b339a1e01451c6720868bc" +dependencies = [ + "regex", + "thiserror", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-html" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "017822b6bd42843c4bd67fabb834f61ce23254e866282dd93871350fd6b7fa1d" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-json" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a9a38a9c679b55cc8d17350381ec08d69fa1a17a53fcf197f344516e485ed4d" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-md" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c20d3ef8d202430b644a307e6299d84bf8ed87fa1b796e4638f8805a595060c" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-python" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c93b1b1fbd0d399db3445f51fd3058e43d0b4dcff62ddbdb46e66550978aa5" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-rust" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0832309b0b2b6d33760ce5c0e818cb47e1d72b468516bfe4134408926fa7594" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-toml" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca517f578a98b23d20780247cc2688407fa81effad5b627a5a364ec3339b53e8" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-typescript" +version = "0.20.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8bc1d2c24276a48ef097a71b56888ac9db63717e8f8d0b324668a27fd619670" +dependencies = [ + "cc", + "tree-sitter", +] + [[package]] name = "typenum" version = "1.17.0" @@ -2366,15 +2749,21 @@ dependencies = [ [[package]] name = "url" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", "idna", "percent-encoding", ] +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "vcpkg" version = "0.2.15" @@ -2625,6 +3014,12 @@ dependencies = [ "libc", ] +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" + [[package]] name = "zerocopy" version = "0.7.32" diff --git a/Cargo.toml b/Cargo.toml index b337895b..93fc2ca8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,8 @@ libc = { version = "^0.2" } log = { version = "0.4.19" } nom = { version = "^7.1.0" } radicle = { version = "0.9.0" } -radicle-term = { version = "0.9.0", package = "radicle-term" } +radicle-term = { version = "0.9.0" } +radicle-cli = { version = "0.9.0" } radicle-surf = { version = "0.18.0" } ratatui = { version = "0.26.1", default-features = false, features = ["all-widgets", "termion"] } simple-logging = { version = "2.0.2" } diff --git a/bin/main.rs b/bin/main.rs index 090d00fb..8073784d 100644 --- a/bin/main.rs +++ b/bin/main.rs @@ -1,5 +1,4 @@ mod commands; -mod terminal; use std::ffi::OsString; use std::io; @@ -9,6 +8,8 @@ use std::{iter, process}; use anyhow::anyhow; use radicle::version::Version; + +use radicle_cli::terminal; use radicle_term as term; use commands::*; diff --git a/bin/terminal.rs b/bin/terminal.rs deleted file mode 100644 index b728e005..00000000 --- a/bin/terminal.rs +++ /dev/null @@ -1,143 +0,0 @@ -pub mod args; -pub mod io; - -use std::ffi::OsString; -use std::process; - -pub use args::{Args, Error, Help}; - -use radicle_term as term; - -use radicle::profile::{Home, Profile}; - -/// Context passed to all commands. -pub trait Context { - /// Return the currently active profile, or an error if no profile is active. - fn profile(&self) -> Result; - /// Return the Radicle home. - fn home(&self) -> Result; -} - -impl Context for Profile { - fn profile(&self) -> Result { - Ok(self.clone()) - } - - fn home(&self) -> Result { - Ok(self.home.clone()) - } -} - -pub struct DefaultContext; - -impl Context for DefaultContext { - fn home(&self) -> Result { - radicle::profile::home() - } - - fn profile(&self) -> Result { - match Profile::load() { - Ok(profile) => Ok(profile), - Err(radicle::profile::Error::NotFound(path)) => Err(args::Error::WithHint { - err: anyhow::anyhow!("Radicle profile not found in '{}'.", path.display()), - hint: "To setup your radicle profile, run `rad auth`.", - } - .into()), - Err(radicle::profile::Error::Config(e)) => Err(e.into()), - Err(e) => Err(anyhow::anyhow!("Could not load radicle profile: {e}")), - } - } -} - -/// A command that can be run. -pub trait Command { - /// Run the command, given arguments and a context. - fn run(self, args: A, context: C) -> anyhow::Result<()>; -} - -impl Command for F -where - F: FnOnce(A, C) -> anyhow::Result<()>, -{ - fn run(self, args: A, context: C) -> anyhow::Result<()> { - self(args, context) - } -} - -pub fn run_command_args(help: Help, cmd: C, args: Vec) -> ! -where - A: Args, - C: Command, -{ - let options = match A::from_args(args) { - Ok((opts, unparsed)) => { - if let Err(err) = args::finish(unparsed) { - term::error(err); - process::exit(1); - } - opts - } - Err(err) => { - let hint = match err.downcast_ref::() { - Some(Error::Help) => { - term::help(help.name, help.version, help.description, help.usage); - process::exit(0); - } - Some(Error::HelpManual { name }) => { - let Ok(status) = term::manual(name) else { - term::error(format!("rad-tui {}: failed to load manual page", help.name)); - process::exit(1); - }; - process::exit(status.code().unwrap_or(0)); - } - Some(Error::Usage) => { - term::usage(help.name, help.usage); - process::exit(1); - } - Some(Error::WithHint { hint, .. }) => Some(hint), - None => None, - }; - term::error(format!("rad-tui {}: {err}", help.name)); - - if let Some(hint) = hint { - term::hint(hint); - } - process::exit(1); - } - }; - - match cmd.run(options, DefaultContext) { - Ok(()) => process::exit(0), - Err(err) => { - fail(help.name, &err); - process::exit(1); - } - } -} - -/// Get the default profile. Fails if there is no profile. -pub fn profile() -> Result { - match Profile::load() { - Ok(profile) => Ok(profile), - Err(radicle::profile::Error::NotFound(path)) => Err(args::Error::WithHint { - err: anyhow::anyhow!("Radicle profile not found in '{}'.", path.display()), - hint: "To setup your radicle profile, run `rad auth`.", - } - .into()), - Err(radicle::profile::Error::Config(e)) => Err(e.into()), - Err(e) => Err(anyhow::anyhow!("Could not load radicle profile: {e}")), - } -} - -pub fn fail(_name: &str, error: &anyhow::Error) { - let err = error.to_string(); - let err = err.trim_end(); - - for line in err.lines() { - term::error(line); - } - - if let Some(Error::WithHint { hint, .. }) = error.downcast_ref::() { - term::hint(hint); - } -} diff --git a/bin/terminal/args.rs b/bin/terminal/args.rs deleted file mode 100644 index 206313e8..00000000 --- a/bin/terminal/args.rs +++ /dev/null @@ -1,134 +0,0 @@ -use std::ffi::OsString; -use std::str::FromStr; - -use anyhow::anyhow; - -use radicle::cob::{issue, patch}; -use radicle::crypto; -use radicle::identity::{Did, RepoId}; - -/// Git revision parameter. Supports extended SHA-1 syntax. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Rev(String); - -impl From for Rev { - fn from(value: String) -> Self { - Rev(value) - } -} - -#[derive(thiserror::Error, Debug)] -#[allow(dead_code)] -pub enum Error { - /// If this error is returned from argument parsing, help is displayed. - #[error("help invoked")] - Help, - /// If this error is returned from argument parsing, the manual page is displayed. - #[error("help manual invoked")] - HelpManual { name: &'static str }, - /// If this error is returned from argument parsing, usage is displayed. - #[error("usage invoked")] - Usage, - /// An error with a hint. - #[error("{err}")] - WithHint { - err: anyhow::Error, - hint: &'static str, - }, -} - -pub struct Help { - pub name: &'static str, - pub description: &'static str, - pub version: &'static str, - pub usage: &'static str, -} - -pub trait Args: Sized { - fn from_env() -> anyhow::Result { - let args: Vec<_> = std::env::args_os().skip(1).collect(); - - match Self::from_args(args) { - Ok((opts, unparsed)) => { - self::finish(unparsed)?; - - Ok(opts) - } - Err(err) => Err(err), - } - } - - fn from_args(args: Vec) -> anyhow::Result<(Self, Vec)>; -} - -#[allow(dead_code)] -pub fn parse_value(flag: &str, value: OsString) -> anyhow::Result -where - ::Err: std::error::Error, -{ - value - .into_string() - .map_err(|_| anyhow!("the value specified for '--{}' is not valid UTF-8", flag))? - .parse() - .map_err(|e| anyhow!("invalid value specified for '--{}' ({})", flag, e)) -} - -#[allow(dead_code)] -pub fn format(arg: lexopt::Arg) -> OsString { - match arg { - lexopt::Arg::Long(flag) => format!("--{flag}").into(), - lexopt::Arg::Short(flag) => format!("-{flag}").into(), - lexopt::Arg::Value(val) => val, - } -} - -#[allow(dead_code)] -pub fn finish(unparsed: Vec) -> anyhow::Result<()> { - if let Some(arg) = unparsed.first() { - return Err(anyhow::anyhow!( - "unexpected argument `{}`", - arg.to_string_lossy() - )); - } - Ok(()) -} - -pub fn string(val: &OsString) -> String { - val.to_string_lossy().to_string() -} - -#[allow(dead_code)] -pub fn rev(val: &OsString) -> anyhow::Result { - let s = val.to_str().ok_or(anyhow!("invalid git rev {val:?}"))?; - Ok(Rev::from(s.to_owned())) -} - -#[allow(dead_code)] -pub fn did(val: &OsString) -> anyhow::Result { - let val = val.to_string_lossy(); - let Ok(peer) = Did::from_str(&val) else { - if crypto::PublicKey::from_str(&val).is_ok() { - return Err(anyhow!("expected DID, did you mean 'did:key:{val}'?")); - } else { - return Err(anyhow!("invalid DID '{}', expected 'did:key'", val)); - } - }; - Ok(peer) -} - -pub fn rid(val: &OsString) -> anyhow::Result { - let val = val.to_string_lossy(); - RepoId::from_str(&val).map_err(|_| anyhow!("invalid Repository ID '{}'", val)) -} - -#[allow(dead_code)] -pub fn issue(val: &OsString) -> anyhow::Result { - let val = val.to_string_lossy(); - issue::IssueId::from_str(&val).map_err(|_| anyhow!("invalid Issue ID '{}'", val)) -} - -#[allow(dead_code)] -pub fn patch(val: &OsString) -> anyhow::Result { - let val = val.to_string_lossy(); - patch::PatchId::from_str(&val).map_err(|_| anyhow!("invalid Patch ID '{}'", val)) -} diff --git a/bin/terminal/io.rs b/bin/terminal/io.rs deleted file mode 100644 index e61911f6..00000000 --- a/bin/terminal/io.rs +++ /dev/null @@ -1,64 +0,0 @@ -use radicle::crypto::ssh::keystore::MemorySigner; -use radicle::crypto::{ssh::Keystore, Signer}; -use radicle::profile::env::RAD_PASSPHRASE; -use radicle::profile::Profile; - -use radicle_term::io::*; -use radicle_term::spinner; - -use inquire::validator; - -/// Validates secret key passphrases. -#[derive(Clone)] -pub struct PassphraseValidator { - keystore: Keystore, -} - -#[allow(dead_code)] -impl PassphraseValidator { - /// Create a new validator. - pub fn new(keystore: Keystore) -> Self { - Self { keystore } - } -} - -impl inquire::validator::StringValidator for PassphraseValidator { - fn validate( - &self, - input: &str, - ) -> Result { - let passphrase = Passphrase::from(input.to_owned()); - if self.keystore.is_valid_passphrase(&passphrase)? { - Ok(validator::Validation::Valid) - } else { - Ok(validator::Validation::Invalid( - validator::ErrorMessage::from("Invalid passphrase, please try again"), - )) - } - } -} - -/// Get the signer. First we try getting it from ssh-agent, otherwise we prompt the user, -/// if we're connected to a TTY. -#[allow(dead_code)] -pub fn signer(profile: &Profile) -> anyhow::Result> { - if let Ok(signer) = profile.signer() { - return Ok(signer); - } - let validator = PassphraseValidator::new(profile.keystore.clone()); - let passphrase = match passphrase(validator) { - Ok(p) => p, - Err(inquire::InquireError::NotTTY) => { - return Err(anyhow::anyhow!( - "running in non-interactive mode, please set `{RAD_PASSPHRASE}` to unseal your key", - )); - } - Err(e) => return Err(e.into()), - }; - let spinner = spinner("Unsealing key..."); - let signer = MemorySigner::load(&profile.keystore, Some(passphrase))?; - - spinner.finish(); - - Ok(signer.boxed()) -}