rad: preparation for the rad remote commands

This commits introduces just a couple of helper functions in order
to work with the `rad remote *` commands.

Signed-off-by: Vincenzo Palazzo <vincenzopalazzodev@gmail.com>
This commit is contained in:
Vincenzo Palazzo 2023-04-20 21:04:26 +02:00 committed by Alexis Sellier
parent 442e8a7128
commit 0468a1a757
No known key found for this signature in database
2 changed files with 76 additions and 15 deletions

View File

@ -10,12 +10,14 @@ use std::str::FromStr;
use anyhow::anyhow; use anyhow::anyhow;
use anyhow::Context as _; use anyhow::Context as _;
use thiserror::Error;
use radicle::crypto::ssh; use radicle::crypto::ssh;
use radicle::git; use radicle::git;
use radicle::git::raw as git2; use radicle::git::raw as git2;
use radicle::git::{Version, VERSION_REQUIRED}; use radicle::git::{Version, VERSION_REQUIRED};
use radicle::prelude::{Id, NodeId}; use radicle::prelude::{Id, NodeId};
use radicle::storage::git::transport;
pub use radicle::git::raw::{ pub use radicle::git::raw::{
build::CheckoutBuilder, AnnotatedCommit, Commit, Direction, ErrorCode, MergeAnalysis, build::CheckoutBuilder, AnnotatedCommit, Commit, Direction, ErrorCode, MergeAnalysis,
@ -60,6 +62,45 @@ impl From<String> for Rev {
} }
} }
#[derive(Error, Debug)]
pub enum RemoteError {
#[error("url malformed: {0}")]
ParseUrl(#[from] transport::local::UrlError),
#[error("remote `url` not found")]
MissingUrl,
#[error("remote `name` not found")]
MissingName,
}
#[derive(Clone)]
pub struct Remote<'a> {
pub(crate) name: String,
pub(crate) url: radicle::git::Url,
inner: git2::Remote<'a>,
}
impl Remote<'_> {
pub fn refspecs(&self) -> git2::Refspecs {
self.inner.refspecs()
}
}
impl<'a> TryFrom<git2::Remote<'a>> for Remote<'a> {
type Error = RemoteError;
fn try_from(value: git2::Remote<'a>) -> Result<Self, Self::Error> {
let url = value.url().map_or(Err(RemoteError::MissingUrl), |url| {
Ok(radicle::git::Url::from_str(url)?)
})?;
let name = value.name().ok_or(RemoteError::MissingName)?;
Ok(Self {
name: name.to_owned(),
url,
inner: value,
})
}
}
/// Get the git repository in the current directory. /// Get the git repository in the current directory.
pub fn repository() -> Result<Repository, anyhow::Error> { pub fn repository() -> Result<Repository, anyhow::Error> {
match Repository::open(".") { match Repository::open(".") {
@ -175,24 +216,27 @@ pub fn is_signing_configured(repo: &Path) -> Result<bool, anyhow::Error> {
} }
/// Return the list of radicle remotes for the given repository. /// Return the list of radicle remotes for the given repository.
pub fn remotes(repo: &git2::Repository) -> anyhow::Result<Vec<(String, NodeId)>> { pub fn rad_remotes(repo: &git2::Repository) -> anyhow::Result<Vec<Remote>> {
let mut remotes = Vec::new(); let remotes: Vec<_> = repo
.remotes()?
for name in repo.remotes().iter().flatten().flatten() { .iter()
let remote = repo.find_remote(name)?; .filter_map(|name| {
for refspec in remote.refspecs() { let remote = repo.find_remote(name?).ok()?;
if refspec.direction() != git2::Direction::Fetch { Remote::try_from(remote).ok()
continue; })
} .collect();
if let Some((peer, _)) = refspec.src().and_then(self::parse_remote) {
remotes.push((name.to_owned(), peer));
}
}
}
Ok(remotes) Ok(remotes)
} }
/// Check if the git remote is configured for the `Repository`.
pub fn is_remote(repo: &git2::Repository, alias: &str) -> anyhow::Result<bool> {
match repo.find_remote(alias) {
Ok(_) => Ok(true),
Err(err) if err.code() == git2::ErrorCode::NotFound => Ok(false),
Err(err) => Err(err.into()),
}
}
/// Get the repository's "rad" remote. /// Get the repository's "rad" remote.
pub fn rad_remote(repo: &Repository) -> anyhow::Result<(git2::Remote, Id)> { pub fn rad_remote(repo: &Repository) -> anyhow::Result<(git2::Remote, Id)> {
match radicle::rad::remote(repo) { match radicle::rad::remote(repo) {
@ -204,6 +248,15 @@ pub fn rad_remote(repo: &Repository) -> anyhow::Result<(git2::Remote, Id)> {
} }
} }
pub fn add_remote<'a>(
repo: &'a Repository,
name: &'a str,
url: &'a radicle::git::Url,
) -> anyhow::Result<Remote<'a>> {
let remote = repo.remote(name, &url.to_string())?;
Ok(Remote::try_from(remote)?)
}
/// Setup an upstream tracking branch for the given remote and branch. /// Setup an upstream tracking branch for the given remote and branch.
/// Creates the tracking branch if it does not exist. /// Creates the tracking branch if it does not exist.
/// ///

View File

@ -99,6 +99,14 @@ pub fn rid(val: &OsString) -> anyhow::Result<Id> {
Id::from_str(&val).map_err(|_| anyhow!("invalid Repository ID '{}'", val)) Id::from_str(&val).map_err(|_| anyhow!("invalid Repository ID '{}'", val))
} }
pub fn pubkey(val: &OsString) -> anyhow::Result<NodeId> {
let Ok(did) = did(val) else {
let nid = nid(val)?;
return Ok(nid)
};
Ok(did.as_key().to_owned())
}
pub fn addr(val: &OsString) -> anyhow::Result<Address> { pub fn addr(val: &OsString) -> anyhow::Result<Address> {
let val = val.to_string_lossy(); let val = val.to_string_lossy();
Address::from_str(&val).map_err(|_| anyhow!("invalid address '{}'", val)) Address::from_str(&val).map_err(|_| anyhow!("invalid address '{}'", val))