Radish alpha
h
rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5
Radicle Heartwood Protocol & Stack
Radicle
Git
fetch: introduce domain type RefPrefix
Fintan Halpenny committed 2 months ago
commit 391571178e26bcba2e3e360b4c0db6c4b6f8178c
parent 8070f98
4 files changed +68 -22
modified crates/radicle-fetch/src/stage.rs
@@ -89,6 +89,42 @@ pub mod error {
    }
}

+
/// A `ref-prefix` used in the `ls-refs` step of the fetch protocol.
+
///
+
/// Since the Radicle protocol only wants to filter by very specific references,
+
/// this type captures the possible reference prefixes.
+
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
+
pub(crate) enum RefPrefix {
+
    /// Represents `refs/rad/id`.
+
    RadId,
+
    /// Represents `"refs/namespaces/<namespace>/refs/rad/id"`.
+
    NamespacedRadId { namespace: PublicKey },
+
    /// Represents `"refs/namespaces/<namespace>/refs/rad/sigrefs"`.
+
    NamespacedRadSigrefs { namespace: PublicKey },
+
    /// Represents `"refs/namespaces"`
+
    AllNamespaces,
+
}
+

+
impl RefPrefix {
+
    /// Convert the [`RefPrefix`] into its equivalent [`BString`].
+
    ///
+
    /// See the [`RefPrefix`] variants for their [`BString`] values.
+
    pub fn into_bstring(self) -> BString {
+
        match self {
+
            RefPrefix::RadId => refs::REFS_RAD_ID.as_bstr().into(),
+
            RefPrefix::NamespacedRadId { namespace } => {
+
                radicle::git::refs::storage::id(&namespace).as_bstr().into()
+
            }
+
            RefPrefix::NamespacedRadSigrefs { namespace } => {
+
                radicle::git::refs::storage::sigrefs(&namespace)
+
                    .as_bstr()
+
                    .into()
+
            }
+
            RefPrefix::AllNamespaces => "refs/namespaces".into(),
+
        }
+
    }
+
}
+

/// A [`ProtocolStage`] describes a single roundtrip with the Radicle
/// node that is serving the data.
///
@@ -107,7 +143,7 @@ pub mod error {
///      refdb (in-memory and production).
pub(crate) trait ProtocolStage {
    /// If and how to perform `ls-refs`.
-
    fn ls_refs(&self) -> Option<NonEmpty<BString>>;
+
    fn ls_refs(&self) -> Option<NonEmpty<RefPrefix>>;

    /// Filter a remote-advertised [`Ref`].
    ///
@@ -163,8 +199,8 @@ pub struct CanonicalId {
}

impl ProtocolStage for CanonicalId {
-
    fn ls_refs(&self) -> Option<NonEmpty<BString>> {
-
        Some(NonEmpty::new(refs::REFS_RAD_ID.as_bstr().into()))
+
    fn ls_refs(&self) -> Option<NonEmpty<RefPrefix>> {
+
        Some(NonEmpty::new(RefPrefix::RadId))
    }

    fn ref_filter(&self, r: Ref) -> Option<ReceivedRef> {
@@ -250,17 +286,17 @@ pub struct SpecialRefs {
}

impl ProtocolStage for SpecialRefs {
-
    fn ls_refs(&self) -> Option<NonEmpty<BString>> {
+
    fn ls_refs(&self) -> Option<NonEmpty<RefPrefix>> {
        match &self.followed {
-
            policy::Allowed::All => Some(NonEmpty::new("refs/namespaces".into())),
+
            policy::Allowed::All => Some(NonEmpty::new(RefPrefix::AllNamespaces)),
            policy::Allowed::Followed { remotes } => NonEmpty::collect(
                remotes
                    .iter()
                    .chain(self.delegates.iter())
                    .flat_map(|remote| {
                        [
-
                            BString::from(radicle::git::refs::storage::id(remote).to_string()),
-
                            BString::from(radicle::git::refs::storage::sigrefs(remote).to_string()),
+
                            RefPrefix::NamespacedRadSigrefs { namespace: *remote },
+
                            RefPrefix::NamespacedRadId { namespace: *remote },
                        ]
                    }),
            ),
@@ -331,12 +367,16 @@ pub struct SigrefsAt {
}

impl ProtocolStage for SigrefsAt {
-
    fn ls_refs(&self) -> Option<NonEmpty<BString>> {
+
    fn ls_refs(&self) -> Option<NonEmpty<RefPrefix>> {
        // N.b. the `Oid`s are known but the `rad/sigrefs` are still
        // asked for to mark them for updating the fetch state.
-
        NonEmpty::collect(self.refs_at.iter().map(|refs_at| {
-
            BString::from(radicle::git::refs::storage::sigrefs(&refs_at.remote).to_string())
-
        }))
+
        NonEmpty::collect(
+
            self.refs_at
+
                .iter()
+
                .map(|refs_at| RefPrefix::NamespacedRadSigrefs {
+
                    namespace: refs_at.remote,
+
                }),
+
        )
    }

    // We only asked for `rad/sigrefs` so we should only get
@@ -421,7 +461,7 @@ pub struct DataRefs {
impl ProtocolStage for DataRefs {
    // We don't need to ask for refs since we have all reference names
    // and `Oid`s in `rad/sigrefs`.
-
    fn ls_refs(&self) -> Option<NonEmpty<BString>> {
+
    fn ls_refs(&self) -> Option<NonEmpty<RefPrefix>> {
        None
    }

modified crates/radicle-fetch/src/state.rs
@@ -225,7 +225,7 @@ impl FetchState {
        let refs = match step.ls_refs() {
            Some(refs) => handle
                .transport
-
                .ls_refs(refs.into(), handshake)?
+
                .ls_refs(refs, handshake)?
                .into_iter()
                .filter_map(|r| step.ref_filter(r))
                .collect::<Vec<_>>(),
modified crates/radicle-fetch/src/transport.rs
@@ -20,6 +20,7 @@ use thiserror::Error;

use crate::git::packfile::Keepfile;
use crate::git::repository;
+
use crate::stage::RefPrefix;

/// Open a reader and writer stream to pass to the ls-refs and fetch
/// processes for communicating during their respective protocols.
@@ -104,11 +105,10 @@ where
    /// Perform ls-refs with the server side.
    pub(crate) fn ls_refs(
        &mut self,
-
        mut prefixes: Vec<BString>,
+
        prefixes: impl IntoIterator<Item = RefPrefix>,
        handshake: &handshake::Outcome,
    ) -> Result<Vec<handshake::Ref>, Error> {
-
        prefixes.sort();
-
        prefixes.dedup();
+
        let prefixes = prefixes.into_iter().collect::<BTreeSet<_>>();
        let (read, write) = self.stream.open();
        Ok(ls_refs::run(
            ls_refs::Config {
modified crates/radicle-fetch/src/transport/ls_refs.rs
@@ -1,4 +1,5 @@
use std::borrow::Cow;
+
use std::collections::BTreeSet;
use std::io;

use gix_features::progress::Progress;
@@ -7,6 +8,8 @@ use gix_protocol::ls_refs;
use gix_protocol::transport::Protocol;
use gix_transport::bstr::{BString, ByteVec};

+
use crate::stage::RefPrefix;
+

use super::{agent_name, Connection};

/// Configuration for running an ls-refs process.
@@ -17,7 +20,7 @@ pub struct Config {
    #[allow(dead_code)]
    pub repo: BString,
    /// Ref prefixes for filtering the output of the ls-refs process.
-
    pub prefixes: Vec<BString>,
+
    pub prefixes: BTreeSet<RefPrefix>,
}

/// Run the ls-refs process using the provided `config`.
@@ -51,11 +54,17 @@ where
        )));
    }

+
    let prefixes = config
+
        .prefixes
+
        .into_iter()
+
        .map(|prefix| prefix.into_bstring())
+
        .collect::<BTreeSet<_>>();
+

    let refs = gix_protocol::ls_refs(
        &mut conn,
        capabilities,
        |_caps, args, features| {
-
            for prefix in &config.prefixes {
+
            for prefix in &prefixes {
                let mut arg = BString::from("ref-prefix ");
                arg.push_str(prefix);
                args.push(arg)
@@ -74,10 +83,7 @@ where
        .into_iter()
        .filter(|r| {
            let (refname, _, _) = r.unpack();
-
            config
-
                .prefixes
-
                .iter()
-
                .any(|prefix| refname.starts_with(prefix))
+
            prefixes.iter().any(|prefix| refname.starts_with(prefix))
        })
        .collect();