unassign.rs
1 use std::ffi::OsString; 2 3 use anyhow::anyhow; 4 use nonempty::NonEmpty; 5 6 use radicle::cob; 7 use radicle::cob::issue; 8 use radicle::prelude::Did; 9 use radicle::storage::WriteStorage; 10 11 use crate::terminal as term; 12 use crate::terminal::args::{Args, Error, Help}; 13 14 pub const HELP: Help = Help { 15 name: "unassign", 16 description: "Unassign an issue", 17 version: env!("CARGO_PKG_VERSION"), 18 usage: r#" 19 Usage 20 21 rad unassign <issue-id> --from <did> [<option>...] 22 23 To unassign multiple users from an issue, you may repeat 24 the `--from` option. 25 26 --from <did> Assignee to remove from the issue 27 28 Options 29 30 --help Print help 31 "#, 32 }; 33 34 #[derive(Debug)] 35 pub struct Options { 36 pub id: issue::IssueId, 37 pub from: NonEmpty<Did>, 38 } 39 40 impl Args for Options { 41 fn from_args(args: Vec<OsString>) -> anyhow::Result<(Self, Vec<OsString>)> { 42 use lexopt::prelude::*; 43 44 let mut parser = lexopt::Parser::from_args(args); 45 let mut id: Option<issue::IssueId> = None; 46 let mut from: Vec<Did> = Vec::new(); 47 48 while let Some(arg) = parser.next()? { 49 match arg { 50 Long("help") | Short('h') => { 51 return Err(Error::Help.into()); 52 } 53 Long("from") => { 54 let val = parser.value()?; 55 let did = term::args::did(&val)?; 56 57 from.push(did); 58 } 59 Value(ref val) if id.is_none() => { 60 id = Some(term::args::issue(val)?); 61 } 62 _ => { 63 return Err(anyhow!(arg.unexpected())); 64 } 65 } 66 } 67 68 Ok(( 69 Options { 70 id: id.ok_or_else(|| anyhow!("an issue must be specified"))?, 71 from: NonEmpty::from_vec(from) 72 .ok_or_else(|| anyhow!("an assignee must be specified"))?, 73 }, 74 vec![], 75 )) 76 } 77 } 78 79 pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> { 80 let profile = ctx.profile()?; 81 let (_, id) = radicle::rad::cwd()?; 82 let repo = profile.storage.repository_mut(id)?; 83 let mut issues = issue::Issues::open(&repo)?; 84 let mut issue = issues.get_mut(&options.id).map_err(|e| match e { 85 cob::store::Error::NotFound(_, _) => anyhow!("issue {} not found", options.id), 86 _ => e.into(), 87 })?; 88 let signer = term::signer(&profile)?; 89 let assigned = issue 90 .assigned() 91 .cloned() 92 .filter(|did| !options.from.contains(did)) 93 .collect::<Vec<_>>(); 94 95 issue.assign(assigned, &signer)?; 96 97 Ok(()) 98 }