From 1d51afad285943849653053282c8adc0dde78aa1 Mon Sep 17 00:00:00 2001 From: xphoniex Date: Wed, 9 Nov 2022 13:15:16 +0000 Subject: [PATCH] Add `rad-track`, `rad-untrack` commands Signed-off-by: xphoniex --- radicle-cli/src/commands.rs | 4 ++ radicle-cli/src/commands/help.rs | 7 +- radicle-cli/src/commands/track.rs | 101 ++++++++++++++++++++++++++++ radicle-cli/src/commands/untrack.rs | 89 ++++++++++++++++++++++++ radicle-cli/src/main.rs | 16 +++++ radicle/src/rad.rs | 8 +++ 6 files changed, 221 insertions(+), 4 deletions(-) create mode 100644 radicle-cli/src/commands/track.rs create mode 100644 radicle-cli/src/commands/untrack.rs diff --git a/radicle-cli/src/commands.rs b/radicle-cli/src/commands.rs index 218e411e..97ea2ee1 100644 --- a/radicle-cli/src/commands.rs +++ b/radicle-cli/src/commands.rs @@ -10,3 +10,7 @@ pub mod rad_help; pub mod rad_init; #[path = "commands/self.rs"] pub mod rad_self; +#[path = "commands/track.rs"] +pub mod rad_track; +#[path = "commands/untrack.rs"] +pub mod rad_untrack; diff --git a/radicle-cli/src/commands/help.rs b/radicle-cli/src/commands/help.rs index 30e442b5..ffe24200 100644 --- a/radicle-cli/src/commands/help.rs +++ b/radicle-cli/src/commands/help.rs @@ -3,10 +3,7 @@ use std::ffi::OsString; use crate::terminal as term; use crate::terminal::args::{Args, Error, Help}; -use super::rad_auth; -use super::rad_checkout; -use super::rad_init; -use super::rad_self; +use super::*; pub const HELP: Help = Help { name: "help", @@ -20,6 +17,8 @@ const COMMANDS: &[Help] = &[ rad_init::HELP, rad_checkout::HELP, rad_self::HELP, + rad_track::HELP, + rad_untrack::HELP, HELP, ]; diff --git a/radicle-cli/src/commands/track.rs b/radicle-cli/src/commands/track.rs new file mode 100644 index 00000000..610888e8 --- /dev/null +++ b/radicle-cli/src/commands/track.rs @@ -0,0 +1,101 @@ +use std::ffi::OsString; + +use anyhow::{anyhow, Context as _}; + +use radicle::identity::project::Id; +use radicle::node::Handle; +use radicle::prelude::*; +use radicle::storage::WriteStorage; + +use crate::terminal as term; +use crate::terminal::args::{Args, Error, Help}; + +pub const HELP: Help = Help { + name: "track", + description: "Manage radicle project tracking relationships", + version: env!("CARGO_PKG_VERSION"), + usage: r#" +Usage + + rad track [] [--fetch] + +Options + + --fetch Fetch the peer's refs into the working copy + --verbose, -v Verbose output + --help Print help +"#, +}; + +#[derive(Debug)] +pub struct Options { + pub id: Option, + pub fetch: bool, + pub verbose: bool, +} + +impl Args for Options { + fn from_args(args: Vec) -> anyhow::Result<(Self, Vec)> { + use lexopt::prelude::*; + + let mut parser = lexopt::Parser::from_args(args); + let mut id: Option = None; + let mut fetch = true; + let mut verbose = false; + + while let Some(arg) = parser.next()? { + match arg { + Long("no-fetch") => fetch = false, + Long("verbose") | Short('v') => verbose = true, + Value(val) if id.is_none() => { + let val = val.to_string_lossy(); + + if let Ok(val) = Id::from_human(&val) { + id = Some(val); + } else { + return Err(anyhow!("invalid ID '{}'", val)); + } + } + Long("help") => { + return Err(Error::Help.into()); + } + _ => { + return Err(anyhow!(arg.unexpected())); + } + } + } + + Ok((Options { id, fetch, verbose }, vec![])) + } +} + +pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> { + let id = options + .id + .or_else(|| radicle::rad::cwd().ok().map(|(_, id)| id)) + .context("current directory is not a git repository; please supply an ``")?; + let profile = ctx.profile()?; + let storage = &profile.storage; + let Doc { payload, .. } = storage.repository(id)?.project_of(profile.id())?; + let node = radicle::node::connect(&profile.node())?; + + term::info!( + "Establishing 🌱 tracking relationship for {}", + term::format::highlight(&payload.name) + ); + term::blank(); + + let tracked = node.track(&id)?; + term::success!( + "Tracking relationship for {} ({}) {}", + term::format::tertiary(&payload.name), + &id.to_human(), + if tracked { "established" } else { "exists" } + ); + + if options.fetch { + node.fetch(&id)?; + } + + Ok(()) +} diff --git a/radicle-cli/src/commands/untrack.rs b/radicle-cli/src/commands/untrack.rs new file mode 100644 index 00000000..54451357 --- /dev/null +++ b/radicle-cli/src/commands/untrack.rs @@ -0,0 +1,89 @@ +use std::ffi::OsString; + +use anyhow::{anyhow, Context as _}; + +use radicle::identity::project::Id; +use radicle::node::Handle; +use radicle::prelude::*; +use radicle::storage::WriteStorage; + +use crate::terminal as term; +use crate::terminal::args::{Args, Error, Help}; + +pub const HELP: Help = Help { + name: "untrack", + description: "Untrack radicle project peers", + version: env!("CARGO_PKG_VERSION"), + usage: r#" +Usage + + rad untrack [] + +Options + + --help Print help +"#, +}; + +#[derive(Debug)] +pub struct Options { + pub id: Option, +} + +impl Args for Options { + fn from_args(args: Vec) -> anyhow::Result<(Self, Vec)> { + use lexopt::prelude::*; + + let mut parser = lexopt::Parser::from_args(args); + let mut id: Option = None; + + while let Some(arg) = parser.next()? { + match arg { + Value(val) if id.is_none() => { + let val = val.to_string_lossy(); + + if let Ok(val) = Id::from_human(&val) { + id = Some(val); + } else { + return Err(anyhow!("invalid ID '{}'", val)); + } + } + Long("help") => { + return Err(Error::Help.into()); + } + _ => { + return Err(anyhow!(arg.unexpected())); + } + } + } + + Ok((Options { id }, vec![])) + } +} + +pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> { + let id = options + .id + .or_else(|| radicle::rad::cwd().ok().map(|(_, id)| id)) + .context("current directory is not a git repository; please supply an ``")?; + let profile = ctx.profile()?; + let storage = &profile.storage; + let Doc { payload, .. } = storage.repository(id)?.project_of(profile.id())?; + let node = radicle::node::connect(&profile.node())?; + + if node.untrack(&id)? { + term::success!( + "Tracking relationships for {} ({}) removed", + term::format::highlight(payload.name), + &id.to_human() + ); + } else { + term::info!( + "Tracking relationships for {} ({}) doesn't exist", + term::format::highlight(payload.name), + &id.to_human() + ); + } + + Ok(()) +} diff --git a/radicle-cli/src/main.rs b/radicle-cli/src/main.rs index e9b9dd91..7778de91 100644 --- a/radicle-cli/src/main.rs +++ b/radicle-cli/src/main.rs @@ -142,6 +142,22 @@ fn run_other(exe: &str, args: &[OsString]) -> Result<(), Option> args.to_vec(), ); } + "track" => { + term::run_command_args::( + rad_track::HELP, + "Track", + rad_track::run, + args.to_vec(), + ); + } + "untrack" => { + term::run_command_args::( + rad_untrack::HELP, + "Untrack", + rad_untrack::run, + args.to_vec(), + ); + } _ => { let exe = format!("{}-{}", NAME, exe); let status = process::Command::new(exe.clone()).args(args).status(); diff --git a/radicle/src/rad.rs b/radicle/src/rad.rs index 52c8605e..5681febd 100644 --- a/radicle/src/rad.rs +++ b/radicle/src/rad.rs @@ -324,6 +324,14 @@ pub fn remote(repo: &git2::Repository) -> Result<(git2::Remote<'_>, Id), RemoteE Ok((remote, url.repo)) } +/// Get the Id of project in current working directory +pub fn cwd() -> Result<(git2::Repository, Id), RemoteError> { + let repo = git2::Repository::open(Path::new("."))?; + let (_, id) = remote(&repo)?; + + Ok((repo, id)) +} + #[cfg(test)] mod tests { use std::collections::HashMap;