Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
Create `url` module shared between crates
Alexis Sellier committed 3 years ago
commit 657a951df0c6b4de6417176e8295fc14fbe68742
parent 617e0c1877235b4949a280a954ab127db3f3af2a
3 files changed +88 -83
modified radicle-remote-helper/src/lib.rs
@@ -1,6 +1,5 @@
#![allow(clippy::collapsible_if)]
use std::path::PathBuf;
-
use std::str::FromStr;
use std::{env, io, process};

use thiserror::Error;
@@ -8,6 +7,7 @@ use thiserror::Error;
use radicle::crypto::{PublicKey, Signer};
use radicle::node::Handle;
use radicle::ssh;
+
use radicle::storage::git::transport::{Url, UrlError};
use radicle::storage::{ReadRepository, WriteRepository, WriteStorage};

/// The service invoked by git on the remote repository, during a push.
@@ -35,78 +35,6 @@ pub enum Error {
    RemoteUrl(#[from] UrlError),
}

-
#[derive(Debug, Error)]
-
pub enum UrlError {
-
    /// Failed to parse.
-
    #[error(transparent)]
-
    Parse(#[from] radicle::git::url::parse::Error),
-
    /// Unsupported URL scheme.
-
    #[error("{0}: unsupported scheme: expected `rad://`")]
-
    UnsupportedScheme(radicle::git::Url),
-
    /// Missing host.
-
    #[error("{0}: missing id")]
-
    MissingId(radicle::git::Url),
-
    /// Invalid remote repository identifier.
-
    #[error("{0}: id: {1}")]
-
    InvalidId(radicle::git::Url, radicle::identity::IdError),
-
    /// Invalid public key.
-
    #[error("{0}: key: {1}")]
-
    InvalidKey(radicle::git::Url, radicle::crypto::PublicKeyError),
-
}
-

-
/// A git remote URL.
-
///
-
/// `rad://<id>/[<pubkey>]`
-
///
-
/// Eg. `rad://zUBDc1UdoEzbpaGcNXqauQkERJ8r` without the public key,
-
/// and `rad://zUBDc1UdoEzbpaGcNXqauQkERJ8r/zCQTxdZGCzQXWBV3XbY3fgkHM3gfkLGyYMd2nL5R2MxQv` with.
-
///
-
#[derive(Debug)]
-
pub struct Url {
-
    pub id: radicle::identity::Id,
-
    pub public_key: Option<PublicKey>,
-
}
-

-
impl FromStr for Url {
-
    type Err = UrlError;
-

-
    fn from_str(s: &str) -> Result<Self, Self::Err> {
-
        let url: radicle::git::Url = s.as_bytes().try_into()?;
-
        Url::try_from(url)
-
    }
-
}
-

-
impl TryFrom<radicle::git::Url> for Url {
-
    type Error = UrlError;
-

-
    fn try_from(url: radicle::git::Url) -> Result<Self, Self::Error> {
-
        if url.scheme != radicle::git::url::Scheme::Radicle {
-
            return Err(Self::Error::UnsupportedScheme(url));
-
        }
-

-
        let id: radicle::identity::Id = url
-
            .host
-
            .as_ref()
-
            .ok_or_else(|| Self::Error::MissingId(url.clone()))?
-
            .parse()
-
            .map_err(|e| Self::Error::InvalidId(url.clone(), e))?;
-

-
        let public_key: Option<PublicKey> = if url.path.is_empty() {
-
            Ok(None)
-
        } else {
-
            let path = url.path.to_string();
-

-
            path.strip_prefix('/')
-
                .unwrap_or(&path)
-
                .parse()
-
                .map(Some)
-
                .map_err(|e| Self::Error::InvalidKey(url.clone(), e))
-
        }?;
-

-
        Ok(Url { id, public_key })
-
    }
-
}
-

/// Run the radicle remote helper using the given profile.
pub fn run(profile: radicle::Profile) -> Result<(), Box<dyn std::error::Error + 'static>> {
    // `GIT_DIR` is expected to be set, though we aren't using it right now.
modified radicle/src/storage/git/transport.rs
@@ -14,6 +14,8 @@
//!
//! This module is meant to be used by first registering our transport with [`register`] and then
//! adding or removing streams through [`Smart`], which can be obtained via [`Smart::singleton`].
+
mod url;
+

use std::collections::HashMap;
use std::str::FromStr;
use std::sync::atomic;
@@ -25,6 +27,8 @@ use once_cell::sync::Lazy;
use crate::git;
use crate::identity::Id;

+
pub use url::{Url, UrlError};
+

/// The map of git smart sub-transport streams. We keep a global map because we have
/// no control over how [`git2::transport::register`] instantiates our [`Smart`] transport
/// or its underlying streams.
@@ -75,16 +79,9 @@ impl git2::transport::SmartSubtransport for Smart {
        url: &str,
        action: git2::transport::Service,
    ) -> Result<Box<dyn git2::transport::SmartSubtransportStream>, git2::Error> {
-
        let url = git::Url::from_bytes(url.as_bytes())
-
            .map_err(|e| git2::Error::from_str(e.to_string().as_str()))?;
-
        let id = Id::from_str(url.host.unwrap_or_default().as_str())
-
            .map_err(|_| git2::Error::from_str("Git URL does not contain a valid project id"))?;
-

-
        if url.scheme != git::url::Scheme::Radicle {
-
            return Err(git2::Error::from_str("Git URL scheme must be `rad`"));
-
        }
+
        let url = Url::from_str(url).map_err(|e| git2::Error::from_str(e.to_string().as_str()))?;

-
        if let Some(stream) = self.take(&id) {
+
        if let Some(stream) = self.take(&url.id) {
            match action {
                git2::transport::Service::UploadPackLs => {}
                git2::transport::Service::UploadPack => {}
@@ -102,7 +99,8 @@ impl git2::transport::SmartSubtransport for Smart {
            Ok(stream)
        } else {
            Err(git2::Error::from_str(&format!(
-
                "repository {id} does not have an associated stream"
+
                "repository {} does not have an associated stream",
+
                url.id
            )))
        }
    }
added radicle/src/storage/git/transport/url.rs
@@ -0,0 +1,79 @@
+
//! Git transport URLs.
+
use std::str::FromStr;
+

+
use thiserror::Error;
+

+
use crate::crypto::PublicKey;
+
use crate::{crypto, git, identity};
+

+
#[derive(Debug, Error)]
+
pub enum UrlError {
+
    /// Failed to parse.
+
    #[error(transparent)]
+
    Parse(#[from] git::url::parse::Error),
+
    /// Unsupported URL scheme.
+
    #[error("{0}: unsupported scheme: expected `rad://`")]
+
    UnsupportedScheme(git::Url),
+
    /// Missing host.
+
    #[error("{0}: missing id")]
+
    MissingId(git::Url),
+
    /// Invalid remote repository identifier.
+
    #[error("{0}: id: {1}")]
+
    InvalidId(git::Url, identity::IdError),
+
    /// Invalid public key.
+
    #[error("{0}: key: {1}")]
+
    InvalidKey(git::Url, crypto::PublicKeyError),
+
}
+

+
/// A git remote URL.
+
///
+
/// `rad://<id>/[<pubkey>]`
+
///
+
/// Eg. `rad://zUBDc1UdoEzbpaGcNXqauQkERJ8r` without the public key,
+
/// and `rad://zUBDc1UdoEzbpaGcNXqauQkERJ8r/zCQTxdZGCzQXWBV3XbY3fgkHM3gfkLGyYMd2nL5R2MxQv` with.
+
///
+
#[derive(Debug)]
+
pub struct Url {
+
    pub id: identity::Id,
+
    pub public_key: Option<PublicKey>,
+
}
+

+
impl FromStr for Url {
+
    type Err = UrlError;
+

+
    fn from_str(s: &str) -> Result<Self, Self::Err> {
+
        let url: git::Url = s.as_bytes().try_into()?;
+
        Url::try_from(url)
+
    }
+
}
+

+
impl TryFrom<git::Url> for Url {
+
    type Error = UrlError;
+

+
    fn try_from(url: git::Url) -> Result<Self, Self::Error> {
+
        if url.scheme != git::url::Scheme::Radicle {
+
            return Err(Self::Error::UnsupportedScheme(url));
+
        }
+

+
        let id: identity::Id = url
+
            .host
+
            .as_ref()
+
            .ok_or_else(|| Self::Error::MissingId(url.clone()))?
+
            .parse()
+
            .map_err(|e| Self::Error::InvalidId(url.clone(), e))?;
+

+
        let public_key: Option<PublicKey> = if url.path.is_empty() {
+
            Ok(None)
+
        } else {
+
            let path = url.path.to_string();
+

+
            path.strip_prefix('/')
+
                .unwrap_or(&path)
+
                .parse()
+
                .map(Some)
+
                .map_err(|e| Self::Error::InvalidKey(url.clone(), e))
+
        }?;
+

+
        Ok(Url { id, public_key })
+
    }
+
}