From 657a951df0c6b4de6417176e8295fc14fbe68742 Mon Sep 17 00:00:00 2001 From: Alexis Sellier Date: Wed, 19 Oct 2022 14:30:01 +0200 Subject: [PATCH] Create `url` module shared between crates Signed-off-by: Alexis Sellier --- radicle-remote-helper/src/lib.rs | 74 +--------------------- radicle/src/storage/git/transport.rs | 18 +++--- radicle/src/storage/git/transport/url.rs | 79 ++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 83 deletions(-) create mode 100644 radicle/src/storage/git/transport/url.rs diff --git a/radicle-remote-helper/src/lib.rs b/radicle-remote-helper/src/lib.rs index 0c15250a..5a7b56eb 100644 --- a/radicle-remote-helper/src/lib.rs +++ b/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:///[]` -/// -/// 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, -} - -impl FromStr for Url { - type Err = UrlError; - - fn from_str(s: &str) -> Result { - let url: radicle::git::Url = s.as_bytes().try_into()?; - Url::try_from(url) - } -} - -impl TryFrom for Url { - type Error = UrlError; - - fn try_from(url: radicle::git::Url) -> Result { - 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 = 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> { // `GIT_DIR` is expected to be set, though we aren't using it right now. diff --git a/radicle/src/storage/git/transport.rs b/radicle/src/storage/git/transport.rs index ea02200f..0cca7800 100644 --- a/radicle/src/storage/git/transport.rs +++ b/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, 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"))?; + let url = Url::from_str(url).map_err(|e| git2::Error::from_str(e.to_string().as_str()))?; - if url.scheme != git::url::Scheme::Radicle { - return Err(git2::Error::from_str("Git URL scheme must be `rad`")); - } - - 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 ))) } } diff --git a/radicle/src/storage/git/transport/url.rs b/radicle/src/storage/git/transport/url.rs new file mode 100644 index 00000000..08ee70f7 --- /dev/null +++ b/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:///[]` +/// +/// 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, +} + +impl FromStr for Url { + type Err = UrlError; + + fn from_str(s: &str) -> Result { + let url: git::Url = s.as_bytes().try_into()?; + Url::try_from(url) + } +} + +impl TryFrom for Url { + type Error = UrlError; + + fn try_from(url: git::Url) -> Result { + 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 = 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 }) + } +}