Radish alpha
h
rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5
Radicle Heartwood Protocol & Stack
Radicle
Git
cli: Clean up handling of RID arguments
Merged lorenz opened 14 days ago
15 files changed +74 -89 07c62449 bb97414a
modified crates/radicle-cli/src/commands/fork.rs
@@ -1,10 +1,11 @@
mod args;

-
use anyhow::Context as _;
-

use radicle::rad;

-
use crate::{terminal as term, warning};
+
use crate::{
+
    terminal::{self as term, args::rid_or_cwd},
+
    warning,
+
};

pub use args::Args;

@@ -13,15 +14,7 @@ pub fn run(args: Args, ctx: impl term::Context) -> anyhow::Result<()> {
    let profile = ctx.profile()?;
    let signer = profile.signer()?;
    let storage = &profile.storage;
-

-
    let rid = match args.rid {
-
        Some(rid) => rid,
-
        None => {
-
            let (_, rid) = rad::cwd().context("Current directory is not a Radicle repository")?;
-

-
            rid
-
        }
-
    };
+
    let (_, rid) = rid_or_cwd(args.repo)?;

    rad::fork(rid, &signer, &storage)?;
    term::success!("Forked repository {rid} for {}", profile.id());
modified crates/radicle-cli/src/commands/fork/args.rs
@@ -19,7 +19,7 @@ pub struct Args {
    ///
    /// [example values: rad:z3Tr6bC7ctEg2EHmLvknUr29mEDLH, z3Tr6bC7ctEg2EHmLvknUr29mEDLH]
    #[arg(value_name = "RID")]
-
    pub(super) rid: Option<RepoId>,
+
    pub(super) repo: Option<RepoId>,
}

#[cfg(test)]
modified crates/radicle-cli/src/commands/id.rs
@@ -17,7 +17,7 @@ use radicle_term::Element;
use crate::git::Rev;
use crate::git::unified_diff::Encode as _;
use crate::terminal as term;
-
use crate::terminal::args::Error;
+
use crate::terminal::args::{Error, rid_or_cwd};
use crate::terminal::format::Author;
use crate::terminal::patch::Message;

@@ -27,12 +27,7 @@ use args::Command;
pub fn run(args: Args, ctx: impl term::Context) -> anyhow::Result<()> {
    let profile = ctx.profile()?;
    let storage = &profile.storage;
-
    let rid = if let Some(rid) = args.repo {
-
        rid
-
    } else {
-
        let (_, rid) = radicle::rad::cwd()?;
-
        rid
-
    };
+
    let (_, rid) = rid_or_cwd(args.repo)?;
    let repo = storage
        .repository(rid)
        .context(anyhow!("repository `{rid}` not found in local storage"))?;
modified crates/radicle-cli/src/commands/inbox.rs
@@ -5,8 +5,6 @@ pub use args::Args;
use std::path::Path;
use std::process;

-
use anyhow::anyhow;
-

use localtime::LocalTime;
use radicle::cob::TypedId;
use radicle::git::BranchName;
@@ -23,6 +21,7 @@ use radicle::{Storage, cob, git};
use term::Element as _;

use crate::terminal as term;
+
use crate::terminal::args::rid_or_cwd;
use args::{ClearMode, Command, ListMode, SortBy};

pub fn run(args: Args, ctx: impl term::Context) -> anyhow::Result<()> {
@@ -391,11 +390,8 @@ fn clear(notifs: &mut notifications::StoreWriter, mode: ClearMode) -> anyhow::Re
        ClearMode::ByRepo(rid) => notifs.clear_by_repo(&rid)?,
        ClearMode::All => notifs.clear_all()?,
        ClearMode::Contextual => {
-
            if let Ok((_, rid)) = radicle::rad::cwd() {
-
                notifs.clear_by_repo(&rid)?
-
            } else {
-
                return Err(anyhow!("not a radicle repository"));
-
            }
+
            let (_, rid) = rid_or_cwd(None)?;
+
            notifs.clear_by_repo(&rid)?
        }
    };
    if cleared > 0 {
modified crates/radicle-cli/src/commands/issue.rs
@@ -26,7 +26,7 @@ use crate::git::Rev;
use crate::node;
use crate::terminal as term;
use crate::terminal::Element;
-
use crate::terminal::args::Error;
+
use crate::terminal::args::{Error, rid_or_cwd};
use crate::terminal::format::Author;
use crate::terminal::issue::Format;

@@ -34,11 +34,7 @@ const ABOUT: &str = "Manage issues";

pub fn run(args: Args, ctx: impl term::Context) -> anyhow::Result<()> {
    let profile = ctx.profile()?;
-
    let rid = match args.repo {
-
        Some(rid) => rid,
-
        None => radicle::rad::cwd().map(|(_, rid)| rid)?,
-
    };
-

+
    let (_, rid) = rid_or_cwd(args.repo)?;
    let repo = profile.storage.repository_mut(rid)?;

    // Fallback to [`Command::List`] if no subcommand is provided.
modified crates/radicle-cli/src/commands/node.rs
@@ -72,7 +72,11 @@ pub fn run(args: Args, ctx: impl term::Context) -> anyhow::Result<()> {

            events::run(node, count, timeout)?;
        }
-
        Command::Routing { rid, nid, json } => {
+
        Command::Routing {
+
            repo: rid,
+
            nid,
+
            json,
+
        } => {
            let store = profile.database()?;
            routing::run(&store, rid, nid, json)?;
        }
modified crates/radicle-cli/src/commands/node/args.rs
@@ -147,8 +147,8 @@ pub(super) enum Command {
        json: bool,

        /// Show the routing table entries for the given RID
-
        #[arg(long)]
-
        rid: Option<RepoId>,
+
        #[arg(long = "rid", value_name = "RID")]
+
        repo: Option<RepoId>,

        /// Show the routing table entries for the given NID
        #[arg(long)]
modified crates/radicle-cli/src/commands/patch.rs
@@ -30,6 +30,7 @@ use radicle::{Node, prelude::*};
use crate::git::Rev;
use crate::node;
use crate::terminal as term;
+
use crate::terminal::args::rid_or_cwd;
use crate::terminal::patch::Message;

pub use args::Args;
@@ -37,14 +38,7 @@ pub use args::Args;
use args::{AssignArgs, Command, CommentAction, LabelArgs};

pub fn run(args: Args, ctx: impl term::Context) -> anyhow::Result<()> {
-
    let (workdir, rid) = if let Some(rid) = args.repo {
-
        (None, rid)
-
    } else {
-
        radicle::rad::cwd()
-
            .map(|(workdir, rid)| (Some(workdir), rid))
-
            .map_err(|_| anyhow!("this command must be run in the context of a repository"))?
-
    };
-

+
    let (workdir, rid) = rid_or_cwd(args.repo)?;
    let profile = ctx.profile()?;
    let repository = profile.storage.repository(rid)?;

modified crates/radicle-cli/src/commands/publish.rs
@@ -1,6 +1,6 @@
mod args;

-
use anyhow::{Context as _, anyhow};
+
use anyhow::anyhow;

use radicle::cob;
use radicle::identity::{Identity, Visibility};
@@ -8,17 +8,13 @@ use radicle::node::Handle as _;
use radicle::storage::{SignRepository, ValidateRepository, WriteRepository, WriteStorage};

use crate::terminal as term;
+
use crate::terminal::args::rid_or_cwd;

pub use args::Args;

pub fn run(args: Args, ctx: impl term::Context) -> anyhow::Result<()> {
    let profile = ctx.profile()?;
-
    let rid = match args.rid {
-
        Some(rid) => rid,
-
        None => radicle::rad::cwd()
-
            .map(|(_, rid)| rid)
-
            .context("Current directory is not a Radicle repository")?,
-
    };
+
    let (_, rid) = rid_or_cwd(args.repo)?;

    let repo = profile.storage.repository_mut(rid)?;
    let signer = profile.signer()?;
modified crates/radicle-cli/src/commands/publish/args.rs
@@ -21,7 +21,7 @@ pub struct Args {
    ///
    /// [example values: rad:z3Tr6bC7ctEg2EHmLvknUr29mEDLH, z3Tr6bC7ctEg2EHmLvknUr29mEDLH]
    #[arg(value_name = "RID")]
-
    pub(super) rid: Option<RepoId>,
+
    pub(super) repo: Option<RepoId>,
}

#[cfg(test)]
modified crates/radicle-cli/src/commands/remote.rs
@@ -6,19 +6,19 @@ pub mod rm;

mod args;

-
use anyhow::anyhow;
-

use radicle::storage::ReadStorage;

-
use crate::terminal as term;
use crate::terminal::Context;
+
use crate::terminal::{self as term, args::rid_or_cwd};

pub use args::Args;
use args::{Command, ListOption};

pub fn run(args: Args, ctx: impl Context) -> anyhow::Result<()> {
-
    let (working, rid) = radicle::rad::cwd()
-
        .map_err(|_| anyhow!("this command must be run in the context of a repository"))?;
+
    let (Some(working), rid) = rid_or_cwd(None)? else {
+
        anyhow::bail!("this command must be run in the context of a repository");
+
    };
+

    let profile = ctx.profile()?;
    let command = args
        .command
modified crates/radicle-cli/src/commands/sync.rs
@@ -5,7 +5,7 @@ use std::collections::BTreeMap;
use std::collections::HashSet;
use std::time;

-
use anyhow::{Context as _, anyhow};
+
use anyhow::anyhow;

use radicle::node;
use radicle::node::SyncedAt;
@@ -22,6 +22,7 @@ use radicle_term::Element;
use crate::node::SyncReporting;
use crate::node::SyncSettings;
use crate::terminal as term;
+
use crate::terminal::args::rid_or_cwd;
use crate::terminal::format::Author;
use crate::terminal::{Table, TableOptions};

@@ -40,31 +41,17 @@ pub fn run(args: Args, ctx: impl term::Context) -> anyhow::Result<()> {
    let debug = args.verbose;

    match args.command {
-
        Some(Command::Status { rid, sort_by }) => {
-
            let rid = match rid {
-
                Some(rid) => rid,
-
                None => {
-
                    let (_, rid) = radicle::rad::cwd()
-
                        .context("Current directory is not a Radicle repository")?;
-
                    rid
-
                }
-
            };
+
        Some(Command::Status { repo, sort_by }) => {
+
            let (_, rid) = rid_or_cwd(repo)?;
            sync_status(rid, &mut node, &profile, &sort_by, verbose)?;
        }
        None => match SyncMode::from(args.sync) {
            SyncMode::Repo {
-
                rid,
+
                repo,
                settings,
                direction,
            } => {
-
                let rid = match rid {
-
                    Some(rid) => rid,
-
                    None => {
-
                        let (_, rid) = radicle::rad::cwd()
-
                            .context("Current directory is not a Radicle repository")?;
-
                        rid
-
                    }
-
                };
+
                let (_, rid) = rid_or_cwd(repo)?;
                let settings = settings.clone().with_profile(&profile);

                if matches!(direction, SyncDirection::Fetch | SyncDirection::Both) {
modified crates/radicle-cli/src/commands/sync/args.rs
@@ -107,7 +107,8 @@ pub(super) struct SyncArgs {
    timeout: std::time::Duration,

    /// The repository to perform the synchronizing for [default: cwd]
-
    rid: Option<RepoId>,
+
    #[arg(value_name = "RID")]
+
    repo: Option<RepoId>,

    /// Synchronize with a specific number of seeds
    ///
@@ -178,7 +179,8 @@ pub(super) enum Command {
    #[clap(alias = "s")]
    Status {
        /// The repository to display the status for [default: cwd]
-
        rid: Option<RepoId>,
+
        #[arg(value_name = "RID")]
+
        repo: Option<RepoId>,
        /// Sort the table by column
        #[arg(long, value_name = "FIELD", value_enum, default_value_t)]
        sort_by: SortBy,
@@ -216,7 +218,7 @@ pub(super) enum SyncMode {
    /// Fetch and/or announce a repositories references
    Repo {
        /// The repository being synchronized
-
        rid: Option<RepoId>,
+
        repo: Option<RepoId>,
        /// The settings for fetch/announce
        settings: SyncSettings,
        /// The direction of the synchronization
@@ -244,7 +246,7 @@ impl From<SyncArgs> for SyncMode {
                settings.seeds = args.seeds.into_iter().collect();
            }
            Self::Repo {
-
                rid: args.rid,
+
                repo: args.repo,
                settings,
                direction,
            }
modified crates/radicle-cli/src/commands/watch.rs
@@ -2,7 +2,7 @@ mod args;

use std::{thread, time};

-
use anyhow::{Context as _, anyhow};
+
use anyhow::anyhow;

use radicle::git;
use radicle::git::raw::ErrorExt as _;
@@ -10,6 +10,7 @@ use radicle::prelude::NodeId;
use radicle::storage::{ReadRepository, ReadStorage};

use crate::terminal as term;
+
use crate::terminal::args::rid_or_cwd;

pub use args::Args;

@@ -21,14 +22,7 @@ pub fn run(args: Args, ctx: impl term::Context) -> anyhow::Result<()> {
        .qualified()
        .ok_or_else(|| anyhow!("reference must be fully-qualified, eg. 'refs/heads/master'"))?;
    let nid = args.node.unwrap_or(profile.public_key);
-
    let rid = match args.repo {
-
        Some(rid) => rid,
-
        None => {
-
            let (_, rid) =
-
                radicle::rad::cwd().context("Current directory is not a Radicle repository")?;
-
            rid
-
        }
-
    };
+
    let (_, rid) = rid_or_cwd(args.repo)?;
    let repo = storage.repository(rid)?;
    let now = time::SystemTime::now();
    let timeout = args.timeout();
modified crates/radicle-cli/src/terminal/args.rs
@@ -105,6 +105,34 @@ impl TypedValueParser for ScopeParser {
    }
}

+
/// A wrapper around [`radicle::rad::cwd`] that should be preferred over direct
+
/// calls in this crate.
+
///
+
/// If the given [`Option<RepoId>`] is `Some`, it is returned as is.
+
/// No attempt is made to detect whether the current working directory is
+
/// a Radicle repository (therefore it is also not checked whether the given
+
/// [`RepoId`] matches the [`RepoId`] associated with the current working
+
/// directory), and the returned repository is `None`.
+
///
+
/// Otherwise, i.e, if the given [`Option<RepoId>`] is `None`, an attempt is
+
/// made to detect the the [`RepoId`] associated with the current working
+
/// directory, by callind [`radicle::rad::cwd`]. If this detection fails,
+
/// an error with context is returned.
+
pub(crate) fn rid_or_cwd(
+
    rid: Option<RepoId>,
+
) -> anyhow::Result<(Option<radicle::git::raw::Repository>, RepoId)> {
+
    match rid {
+
        Some(rid) => Ok((None, rid)),
+
        None => {
+
            use anyhow::Context as _;
+

+
            let (repository, rid) =
+
                radicle::rad::cwd().context("Current directory is not a Radicle repository")?;
+
            Ok((Some(repository), rid))
+
        }
+
    }
+
}
+

#[cfg(test)]
mod test {
    use std::str::FromStr;