lib: Switch to radicle-cli and remove duplicate code

This commit is contained in:
Erik Kundt
2024-04-25 12:40:14 +02:00
parent d54f20f1bd
commit bc3686d8af
6 changed files with 412 additions and 356 deletions
Generated
+408 -13
View File
@@ -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"
+2 -1
View File
@@ -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" }
+2 -1
View File
@@ -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::*;
-143
View File
@@ -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<Profile, anyhow::Error>;
/// Return the Radicle home.
fn home(&self) -> Result<Home, std::io::Error>;
}
impl Context for Profile {
fn profile(&self) -> Result<Profile, anyhow::Error> {
Ok(self.clone())
}
fn home(&self) -> Result<Home, std::io::Error> {
Ok(self.home.clone())
}
}
pub struct DefaultContext;
impl Context for DefaultContext {
fn home(&self) -> Result<Home, std::io::Error> {
radicle::profile::home()
}
fn profile(&self) -> Result<Profile, anyhow::Error> {
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<A: Args, C: Context> {
/// Run the command, given arguments and a context.
fn run(self, args: A, context: C) -> anyhow::Result<()>;
}
impl<F, A: Args, C: Context> Command<A, C> 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<A, C>(help: Help, cmd: C, args: Vec<OsString>) -> !
where
A: Args,
C: Command<A, DefaultContext>,
{
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::<Error>() {
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<Profile, anyhow::Error> {
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::<Error>() {
term::hint(hint);
}
}
-134
View File
@@ -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<String> 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<Self> {
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<OsString>) -> anyhow::Result<(Self, Vec<OsString>)>;
}
#[allow(dead_code)]
pub fn parse_value<T: FromStr>(flag: &str, value: OsString) -> anyhow::Result<T>
where
<T as FromStr>::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<OsString>) -> 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<Rev> {
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<Did> {
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<RepoId> {
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<issue::IssueId> {
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<patch::PatchId> {
let val = val.to_string_lossy();
patch::PatchId::from_str(&val).map_err(|_| anyhow!("invalid Patch ID '{}'", val))
}
-64
View File
@@ -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<validator::Validation, inquire::error::CustomUserError> {
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<Box<dyn Signer>> {
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())
}