diff --git a/radicle-cli/examples/rad-issue.md b/radicle-cli/examples/rad-issue.md index bd22da81..3be1435b 100644 --- a/radicle-cli/examples/rad-issue.md +++ b/radicle-cli/examples/rad-issue.md @@ -37,3 +37,12 @@ Note: this can always be undone with the `unassign` subcommand. ``` $ rad unassign de81d97d7fe07a80bfb339200c6af862d4526b6a z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi ``` + +Great, now we have communicated to the world about our car's defect. + +But wait! We've found an important detail about the car's power requirements. +It will help whoever works on a fix. + +``` +$ rad comment de81d97d7fe07a80bfb339200c6af862d4526b6a --message 'The flux capacitor needs 1.21 Gigawatts' +``` diff --git a/radicle-cli/src/commands.rs b/radicle-cli/src/commands.rs index 51e34a37..70fd80fa 100644 --- a/radicle-cli/src/commands.rs +++ b/radicle-cli/src/commands.rs @@ -6,6 +6,8 @@ pub mod rad_auth; pub mod rad_checkout; #[path = "commands/clone.rs"] pub mod rad_clone; +#[path = "commands/comment.rs"] +pub mod rad_comment; #[path = "commands/delegate.rs"] pub mod rad_delegate; #[path = "commands/edit.rs"] diff --git a/radicle-cli/src/commands/comment.rs b/radicle-cli/src/commands/comment.rs new file mode 100644 index 00000000..ac2cd9d6 --- /dev/null +++ b/radicle-cli/src/commands/comment.rs @@ -0,0 +1,116 @@ +use std::ffi::OsString; +use std::str::FromStr; + +use anyhow::anyhow; + +use radicle::cob; +use radicle::cob::issue::Issues; +use radicle::cob::store; +use radicle::prelude::*; +use radicle::storage; +use radicle::storage::WriteStorage; + +use crate::terminal as term; +use crate::terminal::args::{Args, Error, Help}; +use crate::terminal::patch::Comment; + +pub const HELP: Help = Help { + name: "comment", + description: env!("CARGO_PKG_DESCRIPTION"), + version: env!("CARGO_PKG_VERSION"), + usage: r#" +Usage + + rad comment [-m ] + +Options + + -m, --message Comment message + --help Print help +"#, +}; + +#[derive(Debug)] +pub struct Options { + pub id: cob::ObjectId, + pub message: Comment, +} + +#[inline] +fn parse_cob_id(val: OsString) -> anyhow::Result { + let val = val + .to_str() + .ok_or_else(|| anyhow!("object id specified is not UTF-8"))?; + cob::ObjectId::from_str(val).map_err(|_| anyhow!("invalid object id '{}'", val)) +} + +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 message = Comment::default(); + + while let Some(arg) = parser.next()? { + match arg { + // Options. + Long("message") | Short('m') => { + if message != Comment::Blank { + // We skip this code when `no-message` is specified. + let txt: String = parser.value()?.to_string_lossy().into(); + message.append(&txt); + } + } + Long("no-message") => message = Comment::Blank, + + // Common. + Long("help") => return Err(Error::Help.into()), + + Value(val) if id.is_none() => id = Some(parse_cob_id(val)?), + _ => return Err(anyhow::anyhow!(arg.unexpected())), + } + } + + Ok(( + Options { + id: id.ok_or_else(|| anyhow!("an issue id to comment on must be provided"))?, + message, + }, + vec![], + )) + } +} + +fn comment( + options: &Options, + repo: &storage::git::Repository, + signer: impl Signer, +) -> anyhow::Result<()> { + let message = options.message.clone().get("Enter a comment..."); + if message.is_empty() { + return Ok(()); + } + + let mut issues = Issues::open(*signer.public_key(), repo)?; + let mut issue = issues.get_mut(&options.id).map_err(|e| match e { + store::Error::NotFound(_, _) => anyhow::anyhow!("Could not find issue {}", options.id), + _ => e.into(), + })?; + let (comment_id, _) = issue.root().expect("root comment always exists"); + + issue.comment(message, *comment_id, &signer)?; + Ok(()) +} + +pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> { + let (_, id) = radicle::rad::cwd() + .map_err(|_| anyhow!("this command must be run in the context of a project"))?; + let profile = ctx.profile()?; + let repo = profile.storage.repository(id)?; + let signer = term::signer(&profile)?; + + comment(&options, &repo, signer)?; + + Ok(()) +} diff --git a/radicle-cli/src/main.rs b/radicle-cli/src/main.rs index 51695e11..acc4fd91 100644 --- a/radicle-cli/src/main.rs +++ b/radicle-cli/src/main.rs @@ -134,6 +134,14 @@ fn run_other(exe: &str, args: &[OsString]) -> Result<(), Option> args.to_vec(), ); } + "comment" => { + term::run_command_args::( + rad_comment::HELP, + "Comment", + rad_comment::run, + args.to_vec(), + ); + } "delegate" => { term::run_command_args::( rad_delegate::HELP,