cli: unify label and unlabel
Make the CLI easier by unifying the label and unlabel, using `--add` and `--remove` options instead. Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com> X-Clacks-Overhead: GNU Terry Pratchett
This commit is contained in:
parent
d32a10b4b9
commit
67cbf15e45
|
|
@ -52,7 +52,7 @@ duplicated. While we're at it, let's add a label.
|
||||||
|
|
||||||
```
|
```
|
||||||
$ rad issue assign d185ee1 --add did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
$ rad issue assign d185ee1 --add did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||||
$ rad issue label d185ee1 -l good-first-issue
|
$ rad issue label d185ee1 --add good-first-issue
|
||||||
```
|
```
|
||||||
|
|
||||||
It will now show in the list of issues assigned to us, along with the new label.
|
It will now show in the list of issues assigned to us, along with the new label.
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ We can also label patches as well as assign DIDs to the patch to help
|
||||||
organise your workflow:
|
organise your workflow:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ rad patch label 6ff4f09 -l fun
|
$ rad patch label 6ff4f09 --add fun
|
||||||
$ rad patch assign 6ff4f09 --add did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
$ rad patch assign 6ff4f09 --add did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||||
$ rad patch show 6ff4f09
|
$ rad patch show 6ff4f09
|
||||||
╭────────────────────────────────────────────────────╮
|
╭────────────────────────────────────────────────────╮
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ use std::str::FromStr;
|
||||||
|
|
||||||
use anyhow::{anyhow, Context as _};
|
use anyhow::{anyhow, Context as _};
|
||||||
|
|
||||||
use nonempty::NonEmpty;
|
|
||||||
use radicle::cob::common::{Label, Reaction};
|
use radicle::cob::common::{Label, Reaction};
|
||||||
use radicle::cob::issue;
|
use radicle::cob::issue;
|
||||||
use radicle::cob::issue::{CloseReason, Issues, State};
|
use radicle::cob::issue::{CloseReason, Issues, State};
|
||||||
|
|
@ -40,7 +39,7 @@ Usage
|
||||||
rad issue open [--title <title>] [--description <text>] [--label <label>] [<option>...]
|
rad issue open [--title <title>] [--description <text>] [--label <label>] [<option>...]
|
||||||
rad issue react <issue-id> [--emoji <char>] [--to <comment>] [<option>...]
|
rad issue react <issue-id> [--emoji <char>] [--to <comment>] [<option>...]
|
||||||
rad issue assign <issue-id> [--add <did>] [--remove <did>] [<option>...]
|
rad issue assign <issue-id> [--add <did>] [--remove <did>] [<option>...]
|
||||||
rad issue label <issue-id> --label <label> [<option>...]
|
rad issue label <issue-id> [--add <label>] [--remove <did>] [<option>...]
|
||||||
rad issue unlabel <issue-id> --label <label> [<option>...]
|
rad issue unlabel <issue-id> --label <label> [<option>...]
|
||||||
rad issue comment <issue-id> [--message <message>] [--reply-to <comment-id>] [<option>...]
|
rad issue comment <issue-id> [--message <message>] [--reply-to <comment-id>] [<option>...]
|
||||||
rad issue show <issue-id> [<option>...]
|
rad issue show <issue-id> [<option>...]
|
||||||
|
|
@ -48,26 +47,26 @@ Usage
|
||||||
|
|
||||||
Assign options
|
Assign options
|
||||||
|
|
||||||
-a, --add <did> Add an assignee to the issue (may be specified multiple times).
|
-a, --add <did> Add an assignee to the issue (may be specified multiple times).
|
||||||
Note: --add will take precedence over --remove
|
Note: --add will take precedence over --remove
|
||||||
|
|
||||||
-r, --remove <did> Remove an assignee from the issue (may be specified multiple times).
|
-r, --remove <did> Remove an assignee from the issue (may be specified multiple times).
|
||||||
Note: --add will take precedence over --remove
|
Note: --add will take precedence over --remove
|
||||||
|
|
||||||
Label options
|
Label options
|
||||||
|
|
||||||
-l, --label Label the issue with the provided label (may be specified multiple times)
|
-a, --add <label> Add a label to the issue (may be specified multiple times).
|
||||||
|
Note: --add will take precedence over --remove
|
||||||
|
|
||||||
Unlabel options
|
-r, --remove <label> Remove a label from the issue (may be specified multiple times).
|
||||||
|
Note: --add will take precedence over --remove
|
||||||
-l, --label Remove the provided label from the issue (may be specified multiple times)
|
|
||||||
|
|
||||||
Options
|
Options
|
||||||
|
|
||||||
--no-announce Don't announce issue to peers
|
--no-announce Don't announce issue to peers
|
||||||
--header Show only the issue header, hiding the comments
|
--header Show only the issue header, hiding the comments
|
||||||
--quiet, -q Don't print anything
|
--quiet, -q Don't print anything
|
||||||
--help Print help
|
--help Print help
|
||||||
"#,
|
"#,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -84,7 +83,6 @@ pub enum OperationName {
|
||||||
React,
|
React,
|
||||||
Show,
|
Show,
|
||||||
State,
|
State,
|
||||||
Unlabel,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Command line Peer argument.
|
/// Command line Peer argument.
|
||||||
|
|
@ -135,11 +133,7 @@ pub enum Operation {
|
||||||
},
|
},
|
||||||
Label {
|
Label {
|
||||||
id: Rev,
|
id: Rev,
|
||||||
labels: NonEmpty<Label>,
|
opts: LabelOptions,
|
||||||
},
|
|
||||||
Unlabel {
|
|
||||||
id: Rev,
|
|
||||||
labels: NonEmpty<Label>,
|
|
||||||
},
|
},
|
||||||
List {
|
List {
|
||||||
assigned: Option<Assigned>,
|
assigned: Option<Assigned>,
|
||||||
|
|
@ -153,6 +147,12 @@ pub struct AssignOptions {
|
||||||
pub remove: BTreeSet<Did>,
|
pub remove: BTreeSet<Did>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, PartialEq, Eq)]
|
||||||
|
pub struct LabelOptions {
|
||||||
|
pub add: BTreeSet<Label>,
|
||||||
|
pub remove: BTreeSet<Label>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Options {
|
pub struct Options {
|
||||||
pub op: Operation,
|
pub op: Operation,
|
||||||
|
|
@ -181,6 +181,7 @@ impl Args for Options {
|
||||||
let mut announce = true;
|
let mut announce = true;
|
||||||
let mut quiet = false;
|
let mut quiet = false;
|
||||||
let mut assign_opts = AssignOptions::default();
|
let mut assign_opts = AssignOptions::default();
|
||||||
|
let mut label_opts = LabelOptions::default();
|
||||||
|
|
||||||
while let Some(arg) = parser.next()? {
|
while let Some(arg) = parser.next()? {
|
||||||
match arg {
|
match arg {
|
||||||
|
|
@ -206,12 +207,7 @@ impl Args for Options {
|
||||||
Long("title") if op == Some(OperationName::Open) => {
|
Long("title") if op == Some(OperationName::Open) => {
|
||||||
title = Some(parser.value()?.to_string_lossy().into());
|
title = Some(parser.value()?.to_string_lossy().into());
|
||||||
}
|
}
|
||||||
Short('l') | Long("label")
|
Short('l') | Long("label") if matches!(op, Some(OperationName::Open)) => {
|
||||||
if matches!(
|
|
||||||
op,
|
|
||||||
Some(OperationName::Open | OperationName::Label | OperationName::Unlabel)
|
|
||||||
) =>
|
|
||||||
{
|
|
||||||
let val = parser.value()?;
|
let val = parser.value()?;
|
||||||
let name = term::args::string(&val);
|
let name = term::args::string(&val);
|
||||||
let label = Label::new(name)?;
|
let label = Label::new(name)?;
|
||||||
|
|
@ -294,6 +290,22 @@ impl Args for Options {
|
||||||
.insert(term::args::did(&parser.value()?)?);
|
.insert(term::args::did(&parser.value()?)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Label options
|
||||||
|
Short('a') | Long("add") if matches!(op, Some(OperationName::Label)) => {
|
||||||
|
let val = parser.value()?;
|
||||||
|
let name = term::args::string(&val);
|
||||||
|
let label = Label::new(name)?;
|
||||||
|
|
||||||
|
label_opts.add.insert(label);
|
||||||
|
}
|
||||||
|
Short('r') | Long("remove") if matches!(op, Some(OperationName::Label)) => {
|
||||||
|
let val = parser.value()?;
|
||||||
|
let name = term::args::string(&val);
|
||||||
|
let label = Label::new(name)?;
|
||||||
|
|
||||||
|
label_opts.remove.insert(label);
|
||||||
|
}
|
||||||
|
|
||||||
Long("quiet") | Short('q') => {
|
Long("quiet") | Short('q') => {
|
||||||
quiet = true;
|
quiet = true;
|
||||||
}
|
}
|
||||||
|
|
@ -308,7 +320,6 @@ impl Args for Options {
|
||||||
"s" | "state" => op = Some(OperationName::State),
|
"s" | "state" => op = Some(OperationName::State),
|
||||||
"assign" => op = Some(OperationName::Assign),
|
"assign" => op = Some(OperationName::Assign),
|
||||||
"label" => op = Some(OperationName::Label),
|
"label" => op = Some(OperationName::Label),
|
||||||
"unlabel" => op = Some(OperationName::Unlabel),
|
|
||||||
|
|
||||||
unknown => anyhow::bail!("unknown operation '{}'", unknown),
|
unknown => anyhow::bail!("unknown operation '{}'", unknown),
|
||||||
},
|
},
|
||||||
|
|
@ -361,13 +372,7 @@ impl Args for Options {
|
||||||
},
|
},
|
||||||
OperationName::Label => Operation::Label {
|
OperationName::Label => Operation::Label {
|
||||||
id: id.ok_or_else(|| anyhow!("an issue to label must be provided"))?,
|
id: id.ok_or_else(|| anyhow!("an issue to label must be provided"))?,
|
||||||
labels: NonEmpty::from_vec(labels)
|
opts: label_opts,
|
||||||
.ok_or_else(|| anyhow!("at least one label must be specified"))?,
|
|
||||||
},
|
|
||||||
OperationName::Unlabel => Operation::Unlabel {
|
|
||||||
id: id.ok_or_else(|| anyhow!("an issue to label must be provided"))?,
|
|
||||||
labels: NonEmpty::from_vec(labels)
|
|
||||||
.ok_or_else(|| anyhow!("at least one label must be specified"))?,
|
|
||||||
},
|
},
|
||||||
OperationName::List => Operation::List { assigned, state },
|
OperationName::List => Operation::List { assigned, state },
|
||||||
};
|
};
|
||||||
|
|
@ -397,7 +402,6 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||||
| Operation::Delete { .. }
|
| Operation::Delete { .. }
|
||||||
| Operation::Assign { .. }
|
| Operation::Assign { .. }
|
||||||
| Operation::Label { .. }
|
| Operation::Label { .. }
|
||||||
| Operation::Unlabel { .. }
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut node = Node::new(profile.socket());
|
let mut node = Node::new(profile.socket());
|
||||||
|
|
@ -501,22 +505,18 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
issue.assign(assignees, &signer)?;
|
issue.assign(assignees, &signer)?;
|
||||||
}
|
}
|
||||||
Operation::Label { id, labels } => {
|
Operation::Label {
|
||||||
let id = id.resolve(&repo.backend)?;
|
id,
|
||||||
let Ok(mut issue) = issues.get_mut(&id) else {
|
opts: LabelOptions { add, remove },
|
||||||
anyhow::bail!("Issue `{id}` not found");
|
} => {
|
||||||
};
|
|
||||||
let labels = issue.labels().cloned().chain(labels).collect::<Vec<_>>();
|
|
||||||
issue.label(labels, &signer)?;
|
|
||||||
}
|
|
||||||
Operation::Unlabel { id, labels } => {
|
|
||||||
let id = id.resolve(&repo.backend)?;
|
let id = id.resolve(&repo.backend)?;
|
||||||
let Ok(mut issue) = issues.get_mut(&id) else {
|
let Ok(mut issue) = issues.get_mut(&id) else {
|
||||||
anyhow::bail!("Issue `{id}` not found");
|
anyhow::bail!("Issue `{id}` not found");
|
||||||
};
|
};
|
||||||
let labels = issue
|
let labels = issue
|
||||||
.labels()
|
.labels()
|
||||||
.filter(|&l| !labels.contains(l))
|
.filter(|did| !remove.contains(did))
|
||||||
|
.chain(add.iter())
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
issue.label(labels, &signer)?;
|
issue.label(labels, &signer)?;
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,6 @@ use std::ffi::OsString;
|
||||||
|
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
|
|
||||||
use nonempty::NonEmpty;
|
|
||||||
use radicle::cob::patch::PatchId;
|
use radicle::cob::patch::PatchId;
|
||||||
use radicle::cob::{patch, Label};
|
use radicle::cob::{patch, Label};
|
||||||
use radicle::prelude::*;
|
use radicle::prelude::*;
|
||||||
|
|
@ -57,8 +56,7 @@ Usage
|
||||||
rad patch delete <patch-id> [<option>...]
|
rad patch delete <patch-id> [<option>...]
|
||||||
rad patch redact <revision-id> [<option>...]
|
rad patch redact <revision-id> [<option>...]
|
||||||
rad patch assign <revision-id> [--add <did>] [--remove <did>] [<option>...]
|
rad patch assign <revision-id> [--add <did>] [--remove <did>] [<option>...]
|
||||||
rad patch label <revision-id> --label <label> [<option>...]
|
rad patch label <revision-id> [--add <label>] [--remove <label>] [<option>...]
|
||||||
rad patch unlabel <revision-id> --label <label> [<option>...]
|
|
||||||
rad patch ready <patch-id> [--undo] [<option>...]
|
rad patch ready <patch-id> [--undo] [<option>...]
|
||||||
rad patch edit <patch-id> [<option>...]
|
rad patch edit <patch-id> [<option>...]
|
||||||
rad patch set <patch-id> [<option>...]
|
rad patch set <patch-id> [<option>...]
|
||||||
|
|
@ -79,19 +77,19 @@ Edit options
|
||||||
|
|
||||||
Assign options
|
Assign options
|
||||||
|
|
||||||
-a, --add <did> Add an assignee to the issue (may be specified multiple times).
|
-a, --add <did> Add an assignee to the issue (may be specified multiple times).
|
||||||
Note: --add will take precedence over --remove
|
Note: --add will take precedence over --remove
|
||||||
|
|
||||||
-r, --remove <did> Remove an assignee from the issue (may be specified multiple times).
|
-r, --remove <did> Remove an assignee from the issue (may be specified multiple times).
|
||||||
Note: --add will take precedence over --remove
|
Note: --add will take precedence over --remove
|
||||||
|
|
||||||
Label options
|
Label options
|
||||||
|
|
||||||
-l, --label Label the patch with the provided label (may be specified multiple times)
|
-a, --add <label> Add an assignee to the issue (may be specified multiple times).
|
||||||
|
Note: --add will take precedence over --remove
|
||||||
|
|
||||||
Unlabel options
|
-r, --remove <label> Remove an assignee from the issue (may be specified multiple times).
|
||||||
|
Note: --add will take precedence over --remove
|
||||||
-l, --label Remove the provided label from the patch (may be specified multiple times)
|
|
||||||
|
|
||||||
Update options
|
Update options
|
||||||
|
|
||||||
|
|
@ -137,7 +135,6 @@ pub enum OperationName {
|
||||||
Comment,
|
Comment,
|
||||||
Ready,
|
Ready,
|
||||||
Label,
|
Label,
|
||||||
Unlabel,
|
|
||||||
#[default]
|
#[default]
|
||||||
List,
|
List,
|
||||||
Edit,
|
Edit,
|
||||||
|
|
@ -151,6 +148,12 @@ pub struct AssignOptions {
|
||||||
pub remove: BTreeSet<Did>,
|
pub remove: BTreeSet<Did>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, PartialEq, Eq)]
|
||||||
|
pub struct LabelOptions {
|
||||||
|
pub add: BTreeSet<Label>,
|
||||||
|
pub remove: BTreeSet<Label>,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Filter(fn(&patch::State) -> bool);
|
pub struct Filter(fn(&patch::State) -> bool);
|
||||||
|
|
||||||
impl Filter {
|
impl Filter {
|
||||||
|
|
@ -208,11 +211,7 @@ pub enum Operation {
|
||||||
},
|
},
|
||||||
Label {
|
Label {
|
||||||
patch_id: Rev,
|
patch_id: Rev,
|
||||||
labels: NonEmpty<Label>,
|
opts: LabelOptions,
|
||||||
},
|
|
||||||
Unlabel {
|
|
||||||
patch_id: Rev,
|
|
||||||
labels: NonEmpty<Label>,
|
|
||||||
},
|
},
|
||||||
List {
|
List {
|
||||||
filter: Filter,
|
filter: Filter,
|
||||||
|
|
@ -260,8 +259,8 @@ impl Args for Options {
|
||||||
let mut undo = false;
|
let mut undo = false;
|
||||||
let mut reply_to: Option<Rev> = None;
|
let mut reply_to: Option<Rev> = None;
|
||||||
let mut checkout_opts = checkout::Options::default();
|
let mut checkout_opts = checkout::Options::default();
|
||||||
let mut labels = Vec::new();
|
|
||||||
let mut assign_opts = AssignOptions::default();
|
let mut assign_opts = AssignOptions::default();
|
||||||
|
let mut label_opts = LabelOptions::default();
|
||||||
|
|
||||||
while let Some(arg) = parser.next()? {
|
while let Some(arg) = parser.next()? {
|
||||||
match arg {
|
match arg {
|
||||||
|
|
@ -343,15 +342,21 @@ impl Args for Options {
|
||||||
.insert(term::args::did(&parser.value()?)?);
|
.insert(term::args::did(&parser.value()?)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Un/Label options.
|
// Label options.
|
||||||
Short('l') | Long("label")
|
Short('a') | Long("add") if matches!(op, Some(OperationName::Label)) => {
|
||||||
if matches!(op, Some(OperationName::Label | OperationName::Unlabel)) =>
|
|
||||||
{
|
|
||||||
let val = parser.value()?;
|
let val = parser.value()?;
|
||||||
let name = term::args::string(&val);
|
let name = term::args::string(&val);
|
||||||
let label = Label::new(name)?;
|
let label = Label::new(name)?;
|
||||||
|
|
||||||
labels.push(label);
|
label_opts.add.insert(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
Short('r') | Long("remove") if matches!(op, Some(OperationName::Label)) => {
|
||||||
|
let val = parser.value()?;
|
||||||
|
let name = term::args::string(&val);
|
||||||
|
let label = Label::new(name)?;
|
||||||
|
|
||||||
|
label_opts.remove.insert(label);
|
||||||
}
|
}
|
||||||
|
|
||||||
// List options.
|
// List options.
|
||||||
|
|
@ -403,7 +408,6 @@ impl Args for Options {
|
||||||
"r" | "redact" => op = Some(OperationName::Redact),
|
"r" | "redact" => op = Some(OperationName::Redact),
|
||||||
"assign" => op = Some(OperationName::Assign),
|
"assign" => op = Some(OperationName::Assign),
|
||||||
"label" => op = Some(OperationName::Label),
|
"label" => op = Some(OperationName::Label),
|
||||||
"unlabel" => op = Some(OperationName::Unlabel),
|
|
||||||
"comment" => op = Some(OperationName::Comment),
|
"comment" => op = Some(OperationName::Comment),
|
||||||
"set" => op = Some(OperationName::Set),
|
"set" => op = Some(OperationName::Set),
|
||||||
unknown => anyhow::bail!("unknown operation '{}'", unknown),
|
unknown => anyhow::bail!("unknown operation '{}'", unknown),
|
||||||
|
|
@ -426,7 +430,6 @@ impl Args for Options {
|
||||||
Some(OperationName::Set),
|
Some(OperationName::Set),
|
||||||
Some(OperationName::Assign),
|
Some(OperationName::Assign),
|
||||||
Some(OperationName::Label),
|
Some(OperationName::Label),
|
||||||
Some(OperationName::Unlabel),
|
|
||||||
]
|
]
|
||||||
.contains(&op) =>
|
.contains(&op) =>
|
||||||
{
|
{
|
||||||
|
|
@ -481,13 +484,7 @@ impl Args for Options {
|
||||||
},
|
},
|
||||||
OperationName::Label => Operation::Label {
|
OperationName::Label => Operation::Label {
|
||||||
patch_id: patch_id.ok_or_else(|| anyhow!("a patch must be provided"))?,
|
patch_id: patch_id.ok_or_else(|| anyhow!("a patch must be provided"))?,
|
||||||
labels: NonEmpty::from_vec(labels)
|
opts: label_opts,
|
||||||
.ok_or_else(|| anyhow!("at least one label must be specified"))?,
|
|
||||||
},
|
|
||||||
OperationName::Unlabel => Operation::Unlabel {
|
|
||||||
patch_id: patch_id.ok_or_else(|| anyhow!("a patch must be provided"))?,
|
|
||||||
labels: NonEmpty::from_vec(labels)
|
|
||||||
.ok_or_else(|| anyhow!("at least one label must be specified"))?,
|
|
||||||
},
|
},
|
||||||
OperationName::Set => Operation::Set {
|
OperationName::Set => Operation::Set {
|
||||||
patch_id: patch_id.ok_or_else(|| anyhow!("a patch must be provided"))?,
|
patch_id: patch_id.ok_or_else(|| anyhow!("a patch must be provided"))?,
|
||||||
|
|
@ -602,13 +599,12 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||||
let patch_id = patch_id.resolve(&repository.backend)?;
|
let patch_id = patch_id.resolve(&repository.backend)?;
|
||||||
assign::run(&patch_id, add, remove, &profile, &repository)?;
|
assign::run(&patch_id, add, remove, &profile, &repository)?;
|
||||||
}
|
}
|
||||||
Operation::Label { patch_id, labels } => {
|
Operation::Label {
|
||||||
|
patch_id,
|
||||||
|
opts: LabelOptions { add, remove },
|
||||||
|
} => {
|
||||||
let patch_id = patch_id.resolve(&repository.backend)?;
|
let patch_id = patch_id.resolve(&repository.backend)?;
|
||||||
label::add(&patch_id, labels, &profile, &repository)?;
|
label::run(&patch_id, add, remove, &profile, &repository)?;
|
||||||
}
|
|
||||||
Operation::Unlabel { patch_id, labels } => {
|
|
||||||
let patch_id = patch_id.resolve(&repository.backend)?;
|
|
||||||
label::remove(&patch_id, labels, &profile, &repository)?;
|
|
||||||
}
|
}
|
||||||
Operation::Set { patch_id } => {
|
Operation::Set { patch_id } => {
|
||||||
let patches = radicle::cob::patch::Patches::open(&repository)?;
|
let patches = radicle::cob::patch::Patches::open(&repository)?;
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,13 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use nonempty::NonEmpty;
|
|
||||||
use radicle::storage::git::Repository;
|
use radicle::storage::git::Repository;
|
||||||
|
|
||||||
use crate::terminal as term;
|
use crate::terminal as term;
|
||||||
|
|
||||||
pub fn add(
|
pub fn run(
|
||||||
patch_id: &PatchId,
|
patch_id: &PatchId,
|
||||||
labels: NonEmpty<Label>,
|
add: BTreeSet<Label>,
|
||||||
profile: &Profile,
|
remove: BTreeSet<Label>,
|
||||||
repository: &Repository,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
let signer = term::signer(profile)?;
|
|
||||||
let mut patches = radicle::cob::patch::Patches::open(repository)?;
|
|
||||||
let Ok(mut patch) = patches.get_mut(patch_id) else {
|
|
||||||
anyhow::bail!("Patch `{patch_id}` not found");
|
|
||||||
};
|
|
||||||
let labels = patch.labels().cloned().chain(labels).collect::<Vec<_>>();
|
|
||||||
patch.label(labels, &signer)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove(
|
|
||||||
patch_id: &PatchId,
|
|
||||||
labels: NonEmpty<Label>,
|
|
||||||
profile: &Profile,
|
profile: &Profile,
|
||||||
repository: &Repository,
|
repository: &Repository,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
|
|
@ -34,7 +18,8 @@ pub fn remove(
|
||||||
};
|
};
|
||||||
let labels = patch
|
let labels = patch
|
||||||
.labels()
|
.labels()
|
||||||
.filter(|&l| !labels.contains(l))
|
.filter(|l| !remove.contains(l))
|
||||||
|
.chain(add.iter())
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
patch.label(labels, &signer)?;
|
patch.label(labels, &signer)?;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue