cli: Share common argument types used in `(un)block`

Since `rad block` and `rad unblock` share the same argument type,
`BlockTarget` is moved to the exising `terminal/args.rs` module.
This commit is contained in:
Erik Kundt 2025-10-14 12:39:51 +02:00 committed by Fintan Halpenny
parent cef0ff571d
commit faf19af9de
3 changed files with 69 additions and 41 deletions

View File

@ -4,7 +4,7 @@ use radicle::node::policy::Policy;
use crate::terminal as term;
use args::Target;
use term::args::BlockTarget;
pub use args::Args;
pub(crate) use args::ABOUT;
@ -14,8 +14,8 @@ pub fn run(args: Args, ctx: impl term::Context) -> anyhow::Result<()> {
let mut policies = profile.policies_mut()?;
let updated = match args.target {
Target::Node(nid) => policies.set_follow_policy(&nid, Policy::Block)?,
Target::Repo(rid) => policies.set_seed_policy(&rid, Policy::Block)?,
BlockTarget::Node(nid) => policies.set_follow_policy(&nid, Policy::Block)?,
BlockTarget::Repo(rid) => policies.set_seed_policy(&rid, Policy::Block)?,
};
if updated {
term::success!("Policy for {} set to 'block'", args.target);

View File

@ -1,44 +1,9 @@
use clap::Parser;
use thiserror::Error;
use radicle::prelude::{NodeId, RepoId};
use crate::terminal::args::BlockTarget;
pub(crate) const ABOUT: &str = "Block repositories or nodes from being seeded or followed";
#[derive(Clone, Debug)]
pub(super) enum Target {
Node(NodeId),
Repo(RepoId),
}
#[derive(Debug, Error)]
#[error("invalid repository or node specified (RID parsing failed with: '{repo}', NID parsing failed with: '{node}'))")]
pub(super) struct ParseTargetError {
repo: radicle::identity::IdError,
node: radicle::crypto::PublicKeyError,
}
impl std::str::FromStr for Target {
type Err = ParseTargetError;
fn from_str(val: &str) -> Result<Self, Self::Err> {
val.parse::<RepoId>().map(Target::Repo).or_else(|repo| {
val.parse::<NodeId>()
.map(Target::Node)
.map_err(|node| ParseTargetError { repo, node })
})
}
}
impl std::fmt::Display for Target {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Node(nid) => nid.fmt(f),
Self::Repo(rid) => rid.fmt(f),
}
}
}
#[derive(Parser, Debug)]
#[command(about = ABOUT, disable_version_flag = true)]
pub struct Args {
@ -46,7 +11,7 @@ pub struct Args {
///
/// [example values: rad:z3Tr6bC7ctEg2EHmLvknUr29mEDLH, z6MkiswaKJ85vafhffCGBu2gdBsYoDAyHVBWRxL3j297fwS9]
#[arg(value_name = "RID|NID")]
pub(super) target: Target,
pub(super) target: BlockTarget,
}
#[cfg(test)]

View File

@ -4,8 +4,8 @@ use std::str::FromStr;
use std::time;
use anyhow::anyhow;
use clap::builder::TypedValueParser;
use thiserror::Error;
use radicle::cob::{self, issue, patch};
use radicle::crypto;
@ -207,6 +207,43 @@ pub fn cob(val: &OsString) -> anyhow::Result<cob::ObjectId> {
cob::ObjectId::from_str(&val).map_err(|_| anyhow!("invalid Object ID '{}'", val))
}
/// Targets used in the `block` and `unblock` commands
#[derive(Clone, Debug)]
pub(crate) enum BlockTarget {
Node(NodeId),
Repo(RepoId),
}
#[derive(Debug, Error)]
#[error("invalid repository or node specified (RID parsing failed with: '{repo}', NID parsing failed with: '{node}'))")]
pub(crate) struct BlockTargetParseError {
repo: radicle::identity::IdError,
node: radicle::crypto::PublicKeyError,
}
impl std::str::FromStr for BlockTarget {
type Err = BlockTargetParseError;
fn from_str(val: &str) -> Result<Self, Self::Err> {
val.parse::<RepoId>()
.map(BlockTarget::Repo)
.or_else(|repo| {
val.parse::<NodeId>()
.map(BlockTarget::Node)
.map_err(|node| BlockTargetParseError { repo, node })
})
}
}
impl std::fmt::Display for BlockTarget {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Node(nid) => nid.fmt(f),
Self::Repo(rid) => rid.fmt(f),
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct ScopeParser;
@ -231,3 +268,29 @@ impl TypedValueParser for ScopeParser {
))
}
}
#[cfg(test)]
mod test {
use std::str::FromStr;
use super::BlockTarget;
use super::BlockTargetParseError;
#[test]
fn should_parse_nid() {
let target = BlockTarget::from_str("z6MkiswaKJ85vafhffCGBu2gdBsYoDAyHVBWRxL3j297fwS9");
assert!(target.is_ok())
}
#[test]
fn should_parse_rid() {
let target = BlockTarget::from_str("rad:z3Tr6bC7ctEg2EHmLvknUr29mEDLH");
assert!(target.is_ok())
}
#[test]
fn should_not_parse() {
let err = BlockTarget::from_str("bee").unwrap_err();
assert!(matches!(err, BlockTargetParseError { .. }));
}
}