cli: add remaining patch actions
Add the remaining `Patch` actions to the CLI interface. These include: - React to a revision - Edit a comment - React to a comment - Redact a comment The `react` subcommand is added in a straight-forward manner. The comment commands are slightly less straight-forward. In order to keep to one layer of subcommands, these variants are done through the `rad patch comment` subcommand. They are accessed via options: `--edit`, `--react`, and `--redact`, respectively. Each of these options takes the `CommentId` for which the action is affecting. The `message`, `react` (introduced for `rad patch react`), and `undo` variables repurposed for this subset of actions.
This commit is contained in:
parent
7d28d1e648
commit
4cced3ddb8
|
|
@ -143,6 +143,23 @@ $ rad patch comment aa45913 --message 'My favorite decade!' --reply-to 686ec1c -
|
|||
f4336e42daf76342f787d574b5ee779d89d05c7a
|
||||
```
|
||||
|
||||
If we realize we made a mistake in the comment, we can go back and edit it:
|
||||
|
||||
```
|
||||
$ rad patch comment aa45913 --edit 686ec1c --message 'I cannot wait to get back to the 80s!' --no-announce
|
||||
╭───────────────────────────────────────╮
|
||||
│ alice (you) now 686ec1c │
|
||||
│ I cannot wait to get back to the 80s! │
|
||||
╰───────────────────────────────────────╯
|
||||
```
|
||||
|
||||
And if we really made a mistake, then we can redact the comment entirely:
|
||||
|
||||
```
|
||||
$ rad patch comment aa45913 --redact f4336e4 --no-announce
|
||||
✓ Redacted comment f4336e42daf76342f787d574b5ee779d89d05c7a
|
||||
```
|
||||
|
||||
Now, let's checkout the patch that we just created:
|
||||
|
||||
```
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ mod edit;
|
|||
mod label;
|
||||
#[path = "patch/list.rs"]
|
||||
mod list;
|
||||
#[path = "patch/react.rs"]
|
||||
mod react;
|
||||
#[path = "patch/ready.rs"]
|
||||
mod ready;
|
||||
#[path = "patch/redact.rs"]
|
||||
|
|
@ -33,11 +35,12 @@ mod update;
|
|||
|
||||
use std::collections::BTreeSet;
|
||||
use std::ffi::OsString;
|
||||
use std::str::FromStr as _;
|
||||
|
||||
use anyhow::anyhow;
|
||||
|
||||
use radicle::cob::patch::PatchId;
|
||||
use radicle::cob::{patch, Label};
|
||||
use radicle::cob::{patch, Label, Reaction};
|
||||
use radicle::git::RefString;
|
||||
use radicle::patch::cache::Patches as _;
|
||||
use radicle::storage::git::transport;
|
||||
|
|
@ -67,6 +70,7 @@ Usage
|
|||
rad patch resolve <patch-id> [--review <review-id>] [--comment <comment-id>] [--unresolve] [<option>...]
|
||||
rad patch delete <patch-id> [<option>...]
|
||||
rad patch redact <revision-id> [<option>...]
|
||||
rad patch react <patch-id | revision-id> [--react <emoji>] [<option>...]
|
||||
rad patch assign <revision-id> [--add <did>] [--delete <did>] [<option>...]
|
||||
rad patch label <revision-id> [--add <label>] [--delete <label>] [<option>...]
|
||||
rad patch ready <patch-id> [--undo] [<option>...]
|
||||
|
|
@ -89,6 +93,10 @@ Comment options
|
|||
|
||||
-m, --message <string> Provide a comment message via the command-line
|
||||
--reply-to <comment> The comment to reply to
|
||||
--edit <comment> The comment to edit (use --message to edit with the provided message)
|
||||
--react <comment> The comment to react to
|
||||
--emoji <char> The emoji to react with when --react is used
|
||||
--redact <comment> The comment to redact
|
||||
|
||||
Edit options
|
||||
|
||||
|
|
@ -163,6 +171,10 @@ Set options
|
|||
|
||||
--remote <string> Provide the git remote to use as the upstream
|
||||
|
||||
React options
|
||||
|
||||
--emoji <char> The emoji to react to the patch or revision with
|
||||
|
||||
Other options
|
||||
|
||||
--repo <rid> Operate on the given repository (default: cwd)
|
||||
|
|
@ -182,6 +194,7 @@ pub enum OperationName {
|
|||
Delete,
|
||||
Checkout,
|
||||
Comment,
|
||||
React,
|
||||
Ready,
|
||||
Review,
|
||||
Resolve,
|
||||
|
|
@ -194,6 +207,13 @@ pub enum OperationName {
|
|||
Cache,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum CommentOperation {
|
||||
Edit,
|
||||
React,
|
||||
Redact,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq)]
|
||||
pub struct AssignOptions {
|
||||
pub add: BTreeSet<Did>,
|
||||
|
|
@ -243,6 +263,26 @@ pub enum Operation {
|
|||
message: Message,
|
||||
reply_to: Option<Rev>,
|
||||
},
|
||||
CommentEdit {
|
||||
revision_id: Rev,
|
||||
comment_id: Rev,
|
||||
message: Message,
|
||||
},
|
||||
CommentRedact {
|
||||
revision_id: Rev,
|
||||
comment_id: Rev,
|
||||
},
|
||||
CommentReact {
|
||||
revision_id: Rev,
|
||||
comment_id: Rev,
|
||||
reaction: Reaction,
|
||||
undo: bool,
|
||||
},
|
||||
React {
|
||||
revision_id: Rev,
|
||||
reaction: Reaction,
|
||||
undo: bool,
|
||||
},
|
||||
Review {
|
||||
patch_id: Rev,
|
||||
revision_id: Option<Rev>,
|
||||
|
|
@ -291,12 +331,16 @@ impl Operation {
|
|||
| Operation::Ready { .. }
|
||||
| Operation::Delete { .. }
|
||||
| Operation::Comment { .. }
|
||||
| Operation::CommentEdit { .. }
|
||||
| Operation::CommentRedact { .. }
|
||||
| Operation::CommentReact { .. }
|
||||
| Operation::Review { .. }
|
||||
| Operation::Resolve { .. }
|
||||
| Operation::Assign { .. }
|
||||
| Operation::Label { .. }
|
||||
| Operation::Edit { .. }
|
||||
| Operation::Redact { .. }
|
||||
| Operation::React { .. }
|
||||
| Operation::Set { .. } => true,
|
||||
Operation::Show { .. }
|
||||
| Operation::Diff { .. }
|
||||
|
|
@ -338,7 +382,9 @@ impl Args for Options {
|
|||
let mut diff = false;
|
||||
let mut debug = false;
|
||||
let mut undo = false;
|
||||
let mut reaction: Option<Reaction> = None;
|
||||
let mut reply_to: Option<Rev> = None;
|
||||
let mut comment_op: Option<(CommentOperation, Rev)> = None;
|
||||
let mut checkout_opts = checkout::Options::default();
|
||||
let mut remote: Option<RefString> = None;
|
||||
let mut assign_opts = AssignOptions::default();
|
||||
|
|
@ -394,6 +440,17 @@ impl Args for Options {
|
|||
base_id = Some(rev);
|
||||
}
|
||||
|
||||
// React options.
|
||||
Long("emoji") if op == Some(OperationName::React) => {
|
||||
if let Some(emoji) = parser.value()?.to_str() {
|
||||
reaction =
|
||||
Some(Reaction::from_str(emoji).map_err(|_| anyhow!("invalid emoji"))?);
|
||||
}
|
||||
}
|
||||
Long("undo") if op == Some(OperationName::React) => {
|
||||
undo = true;
|
||||
}
|
||||
|
||||
// Comment options.
|
||||
Long("reply-to") if op == Some(OperationName::Comment) => {
|
||||
let val = parser.value()?;
|
||||
|
|
@ -402,6 +459,42 @@ impl Args for Options {
|
|||
reply_to = Some(rev);
|
||||
}
|
||||
|
||||
Long("edit") if op == Some(OperationName::Comment) => {
|
||||
let val = parser.value()?;
|
||||
let rev = term::args::rev(&val)?;
|
||||
|
||||
comment_op = Some((CommentOperation::Edit, rev));
|
||||
}
|
||||
|
||||
Long("react") if op == Some(OperationName::Comment) => {
|
||||
let val = parser.value()?;
|
||||
let rev = term::args::rev(&val)?;
|
||||
|
||||
comment_op = Some((CommentOperation::React, rev));
|
||||
}
|
||||
Long("emoji")
|
||||
if op == Some(OperationName::Comment)
|
||||
&& matches!(comment_op, Some((CommentOperation::React, _))) =>
|
||||
{
|
||||
if let Some(emoji) = parser.value()?.to_str() {
|
||||
reaction =
|
||||
Some(Reaction::from_str(emoji).map_err(|_| anyhow!("invalid emoji"))?);
|
||||
}
|
||||
}
|
||||
Long("undo")
|
||||
if op == Some(OperationName::Comment)
|
||||
&& matches!(comment_op, Some((CommentOperation::React, _))) =>
|
||||
{
|
||||
undo = true;
|
||||
}
|
||||
|
||||
Long("redact") if op == Some(OperationName::Comment) => {
|
||||
let val = parser.value()?;
|
||||
let rev = term::args::rev(&val)?;
|
||||
|
||||
comment_op = Some((CommentOperation::Redact, rev));
|
||||
}
|
||||
|
||||
// Edit options.
|
||||
Long("revision") | Short('r') if op == Some(OperationName::Edit) => {
|
||||
let val = parser.value()?;
|
||||
|
|
@ -672,11 +765,38 @@ impl Args for Options {
|
|||
revision_id,
|
||||
opts: checkout_opts,
|
||||
},
|
||||
OperationName::Comment => Operation::Comment {
|
||||
OperationName::Comment => match comment_op {
|
||||
Some((CommentOperation::Edit, comment)) => Operation::CommentEdit {
|
||||
revision_id: patch_id
|
||||
.ok_or_else(|| anyhow!("a patch or revision must be provided"))?,
|
||||
comment_id: comment,
|
||||
message,
|
||||
},
|
||||
Some((CommentOperation::React, comment)) => Operation::CommentReact {
|
||||
revision_id: patch_id
|
||||
.ok_or_else(|| anyhow!("a patch or revision must be provided"))?,
|
||||
comment_id: comment,
|
||||
reaction: reaction
|
||||
.ok_or_else(|| anyhow!("a reaction emoji must be provided"))?,
|
||||
undo,
|
||||
},
|
||||
Some((CommentOperation::Redact, comment)) => Operation::CommentRedact {
|
||||
revision_id: patch_id
|
||||
.ok_or_else(|| anyhow!("a patch or revision must be provided"))?,
|
||||
comment_id: comment,
|
||||
},
|
||||
None => Operation::Comment {
|
||||
revision_id: patch_id
|
||||
.ok_or_else(|| anyhow!("a patch or revision must be provided"))?,
|
||||
message,
|
||||
reply_to,
|
||||
},
|
||||
},
|
||||
OperationName::React => Operation::React {
|
||||
revision_id: patch_id
|
||||
.ok_or_else(|| anyhow!("a patch or revision must be provided"))?,
|
||||
message,
|
||||
reply_to,
|
||||
reaction: reaction.ok_or_else(|| anyhow!("a reaction emoji must be provided"))?,
|
||||
undo,
|
||||
},
|
||||
OperationName::Review => Operation::Review {
|
||||
patch_id: patch_id
|
||||
|
|
@ -963,6 +1083,52 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
|||
};
|
||||
cache::run(mode, &profile)?;
|
||||
}
|
||||
Operation::CommentEdit {
|
||||
revision_id,
|
||||
comment_id,
|
||||
message,
|
||||
} => {
|
||||
let comment = comment_id.resolve(&repository.backend)?;
|
||||
comment::edit::run(
|
||||
revision_id,
|
||||
comment,
|
||||
message,
|
||||
options.quiet,
|
||||
&repository,
|
||||
&profile,
|
||||
)?;
|
||||
}
|
||||
Operation::CommentRedact {
|
||||
revision_id,
|
||||
comment_id,
|
||||
} => {
|
||||
let comment = comment_id.resolve(&repository.backend)?;
|
||||
comment::redact::run(revision_id, comment, &repository, &profile)?;
|
||||
}
|
||||
Operation::CommentReact {
|
||||
revision_id,
|
||||
comment_id,
|
||||
reaction,
|
||||
undo,
|
||||
} => {
|
||||
let comment = comment_id.resolve(&repository.backend)?;
|
||||
if undo {
|
||||
comment::react::run(revision_id, comment, reaction, false, &repository, &profile)?;
|
||||
} else {
|
||||
comment::react::run(revision_id, comment, reaction, true, &repository, &profile)?;
|
||||
}
|
||||
}
|
||||
Operation::React {
|
||||
revision_id,
|
||||
reaction,
|
||||
undo,
|
||||
} => {
|
||||
if undo {
|
||||
react::run(&revision_id, reaction, false, &repository, &profile)?;
|
||||
} else {
|
||||
react::run(&revision_id, reaction, true, &repository, &profile)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if announce {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
#[path = "comment/edit.rs"]
|
||||
pub mod edit;
|
||||
#[path = "comment/react.rs"]
|
||||
pub mod react;
|
||||
#[path = "comment/redact.rs"]
|
||||
pub mod redact;
|
||||
|
||||
use super::*;
|
||||
|
||||
use radicle::cob;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
use anyhow::anyhow;
|
||||
|
||||
use radicle::cob;
|
||||
use radicle::cob::patch;
|
||||
use radicle::cob::thread;
|
||||
use radicle::patch::cache::Patches as _;
|
||||
use radicle::patch::ByRevision;
|
||||
use radicle::storage::git::Repository;
|
||||
use radicle::Profile;
|
||||
|
||||
use crate::git;
|
||||
use crate::terminal as term;
|
||||
use crate::terminal::Element as _;
|
||||
|
||||
pub fn run(
|
||||
revision_id: git::Rev,
|
||||
comment_id: thread::CommentId,
|
||||
message: term::patch::Message,
|
||||
quiet: bool,
|
||||
repo: &Repository,
|
||||
profile: &Profile,
|
||||
) -> anyhow::Result<()> {
|
||||
let signer = term::signer(profile)?;
|
||||
let mut patches = profile.patches_mut(repo)?;
|
||||
let revision_id = revision_id.resolve::<cob::EntryId>(&repo.backend)?;
|
||||
let ByRevision {
|
||||
id: patch_id,
|
||||
patch,
|
||||
revision_id,
|
||||
revision,
|
||||
} = patches
|
||||
.find_by_revision(&patch::RevisionId::from(revision_id))?
|
||||
.ok_or_else(|| anyhow!("Patch revision `{revision_id}` not found"))?;
|
||||
let (body, _) = super::prompt(message, None, &revision, repo)?;
|
||||
let mut patch = patch::PatchMut::new(patch_id, patch, &mut patches);
|
||||
patch.comment_edit(revision_id, comment_id, body, vec![], &signer)?;
|
||||
|
||||
if !quiet {
|
||||
let comment = patch
|
||||
.revision(&revision_id)
|
||||
.ok_or(anyhow!("error retrieving revision `{revision_id}`"))?
|
||||
.discussion()
|
||||
.comment(&comment_id)
|
||||
.ok_or(anyhow!("error retrieving comment `{comment_id}`"))?;
|
||||
|
||||
term::comment::widget(&comment_id, comment, profile).print();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
use anyhow::anyhow;
|
||||
|
||||
use radicle::cob;
|
||||
use radicle::cob::patch;
|
||||
use radicle::cob::thread;
|
||||
use radicle::cob::Reaction;
|
||||
use radicle::patch::cache::Patches as _;
|
||||
use radicle::patch::ByRevision;
|
||||
use radicle::storage::git::Repository;
|
||||
use radicle::Profile;
|
||||
|
||||
use crate::git;
|
||||
use crate::terminal as term;
|
||||
|
||||
pub fn run(
|
||||
revision_id: git::Rev,
|
||||
comment: thread::CommentId,
|
||||
reaction: Reaction,
|
||||
active: bool,
|
||||
repo: &Repository,
|
||||
profile: &Profile,
|
||||
) -> anyhow::Result<()> {
|
||||
let signer = term::signer(profile)?;
|
||||
let mut patches = profile.patches_mut(repo)?;
|
||||
let revision_id = revision_id.resolve::<cob::EntryId>(&repo.backend)?;
|
||||
let ByRevision {
|
||||
id: patch_id,
|
||||
patch,
|
||||
revision_id,
|
||||
..
|
||||
} = patches
|
||||
.find_by_revision(&patch::RevisionId::from(revision_id))?
|
||||
.ok_or_else(|| anyhow!("Patch revision `{revision_id}` not found"))?;
|
||||
let mut patch = patch::PatchMut::new(patch_id, patch, &mut patches);
|
||||
patch.comment_react(revision_id, comment, reaction, active, &signer)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
use anyhow::anyhow;
|
||||
|
||||
use radicle::cob;
|
||||
use radicle::cob::patch;
|
||||
use radicle::cob::thread;
|
||||
use radicle::patch::cache::Patches as _;
|
||||
use radicle::patch::ByRevision;
|
||||
use radicle::storage::git::Repository;
|
||||
use radicle::Profile;
|
||||
|
||||
use crate::git;
|
||||
use crate::terminal as term;
|
||||
|
||||
pub fn run(
|
||||
revision_id: git::Rev,
|
||||
comment: thread::CommentId,
|
||||
repo: &Repository,
|
||||
profile: &Profile,
|
||||
) -> anyhow::Result<()> {
|
||||
let signer = term::signer(profile)?;
|
||||
let mut patches = profile.patches_mut(repo)?;
|
||||
let revision_id = revision_id.resolve::<cob::EntryId>(&repo.backend)?;
|
||||
let ByRevision {
|
||||
id: patch_id,
|
||||
patch,
|
||||
revision_id,
|
||||
..
|
||||
} = patches
|
||||
.find_by_revision(&patch::RevisionId::from(revision_id))?
|
||||
.ok_or_else(|| anyhow!("Patch revision `{revision_id}` not found"))?;
|
||||
let mut patch = patch::PatchMut::new(patch_id, patch, &mut patches);
|
||||
patch.comment_redact(revision_id, comment, &signer)?;
|
||||
term::success!("Redacted comment {comment}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
use anyhow::anyhow;
|
||||
|
||||
use radicle::cob;
|
||||
use radicle::cob::{patch, Reaction};
|
||||
use radicle::patch::cache::Patches as _;
|
||||
use radicle::patch::ByRevision;
|
||||
use radicle::storage::git::Repository;
|
||||
use radicle::Profile;
|
||||
|
||||
use crate::git;
|
||||
use crate::terminal as term;
|
||||
|
||||
pub fn run(
|
||||
revision_id: &git::Rev,
|
||||
reaction: Reaction,
|
||||
active: bool,
|
||||
repo: &Repository,
|
||||
profile: &Profile,
|
||||
) -> anyhow::Result<()> {
|
||||
let signer = term::signer(profile)?;
|
||||
let mut patches = profile.patches_mut(repo)?;
|
||||
let revision_id = revision_id.resolve::<cob::EntryId>(&repo.backend)?;
|
||||
let ByRevision {
|
||||
id: patch_id,
|
||||
patch,
|
||||
revision_id,
|
||||
..
|
||||
} = patches
|
||||
.find_by_revision(&patch::RevisionId::from(revision_id))?
|
||||
.ok_or_else(|| anyhow!("Patch revision `{revision_id}` not found"))?;
|
||||
let mut patch = patch::PatchMut::new(patch_id, patch, &mut patches);
|
||||
patch.react(revision_id, reaction, None, active, &signer)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Loading…
Reference in New Issue