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::Context as _;
use thiserror::Error;
use radicle::crypto::ssh;
use radicle::git;
use radicle::git::raw as git2;
use radicle::git::{Version, VERSION_REQUIRED};
use radicle::prelude::{Id, NodeId};
use radicle::storage::git::transport;
pub use radicle::git::raw::{
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.
pub fn repository() -> Result<Repository, anyhow::Error> {
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.
pub fn remotes(repo: &git2::Repository) -> anyhow::Result<Vec<(String, NodeId)>> {
let mut remotes = Vec::new();
for name in repo.remotes().iter().flatten().flatten() {
let remote = repo.find_remote(name)?;
for refspec in remote.refspecs() {
if refspec.direction() != git2::Direction::Fetch {
continue;
}
if let Some((peer, _)) = refspec.src().and_then(self::parse_remote) {
remotes.push((name.to_owned(), peer));
}
}
}
pub fn rad_remotes(repo: &git2::Repository) -> anyhow::Result<Vec<Remote>> {
let remotes: Vec<_> = repo
.remotes()?
.iter()
.filter_map(|name| {
let remote = repo.find_remote(name?).ok()?;
Remote::try_from(remote).ok()
})
.collect();
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.
pub fn rad_remote(repo: &Repository) -> anyhow::Result<(git2::Remote, Id)> {
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.
/// 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))
}
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> {
let val = val.to_string_lossy();
Address::from_str(&val).map_err(|_| anyhow!("invalid address '{}'", val))