183 lines
5.5 KiB
Rust
183 lines
5.5 KiB
Rust
use std::collections::BTreeSet;
|
|
use std::fmt;
|
|
use std::fmt::Write as _;
|
|
|
|
use radicle::crypto::PublicKey;
|
|
use radicle::git;
|
|
use radicle::git::refs::storage::{IDENTITY_BRANCH, SIGREFS_BRANCH};
|
|
use radicle::storage;
|
|
use radicle::storage::git::NAMESPACES_GLOB;
|
|
use radicle::storage::{Namespaces, Remote};
|
|
|
|
/// A Git [refspec].
|
|
///
|
|
/// [refspec]: https://git-scm.com/book/en/v2/Git-Internals-The-Refspec
|
|
// TODO(finto): this should go into radicle-git-ext/git-ref-format
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct Refspec<T, U> {
|
|
pub src: T,
|
|
pub dst: U,
|
|
pub force: bool,
|
|
}
|
|
|
|
impl<T, U> fmt::Display for Refspec<T, U>
|
|
where
|
|
T: fmt::Display,
|
|
U: fmt::Display,
|
|
{
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
if self.force {
|
|
f.write_char('+')?;
|
|
}
|
|
write!(f, "{}:{}", self.src, self.dst)
|
|
}
|
|
}
|
|
|
|
/// Radicle special refs, i.e. `refs/rad/*`.
|
|
pub struct SpecialRefs(pub(super) Namespaces);
|
|
|
|
impl AsRefspecs for SpecialRefs {
|
|
fn as_refspecs(&self) -> Vec<Refspec<git::PatternString, git::PatternString>> {
|
|
match &self.0 {
|
|
Namespaces::All => {
|
|
let id = NAMESPACES_GLOB.join(&*IDENTITY_BRANCH);
|
|
let sigrefs = NAMESPACES_GLOB.join(&*SIGREFS_BRANCH);
|
|
vec![
|
|
Refspec {
|
|
src: id.clone(),
|
|
dst: id,
|
|
force: false,
|
|
},
|
|
Refspec {
|
|
src: sigrefs.clone(),
|
|
dst: sigrefs,
|
|
force: false,
|
|
},
|
|
]
|
|
}
|
|
Namespaces::Trusted(pks) => pks.iter().flat_map(rad_refs).collect(),
|
|
}
|
|
}
|
|
|
|
fn into_refspecs(self) -> Vec<Refspec<git::PatternString, git::PatternString>> {
|
|
self.as_refspecs()
|
|
}
|
|
}
|
|
|
|
fn rad_refs(pk: &PublicKey) -> Vec<Refspec<git::PatternString, git::PatternString>> {
|
|
let ns = pk.to_namespace();
|
|
let id = git::PatternString::from(ns.join(&*IDENTITY_BRANCH));
|
|
let id = Refspec {
|
|
src: id.clone(),
|
|
dst: id,
|
|
force: false,
|
|
};
|
|
let sigrefs = git::PatternString::from(ns.join(&*SIGREFS_BRANCH));
|
|
let sigrefs = Refspec {
|
|
src: sigrefs.clone(),
|
|
dst: sigrefs,
|
|
force: false,
|
|
};
|
|
vec![id, sigrefs]
|
|
}
|
|
|
|
/// A conversion trait for producing a set of Git [`Refspec`]s.
|
|
pub trait AsRefspecs
|
|
where
|
|
Self: Sized,
|
|
{
|
|
/// Convert the borrowed data into a set of [`Refspec`]s.
|
|
fn as_refspecs(&self) -> Vec<Refspec<git::PatternString, git::PatternString>>;
|
|
|
|
/// Convert the owned data into a set of [`Refspec`]s.
|
|
///
|
|
/// Nb. The default implementation uses
|
|
/// [`AsRefspecs::as_refspecs`], which may clone data.
|
|
fn into_refspecs(self) -> Vec<Refspec<git::PatternString, git::PatternString>> {
|
|
self.as_refspecs()
|
|
}
|
|
}
|
|
|
|
impl AsRefspecs for Namespaces {
|
|
fn as_refspecs(&self) -> Vec<Refspec<git::PatternString, git::PatternString>> {
|
|
match self {
|
|
Namespaces::All => vec![Refspec {
|
|
src: (*storage::git::NAMESPACES_GLOB).clone(),
|
|
dst: (*storage::git::NAMESPACES_GLOB).clone(),
|
|
force: false,
|
|
}],
|
|
Namespaces::Trusted(pks) => pks
|
|
.iter()
|
|
.map(|pk| {
|
|
let ns = pk.to_namespace().with_pattern(git::refspec::STAR);
|
|
Refspec {
|
|
src: ns.clone(),
|
|
dst: ns,
|
|
force: false,
|
|
}
|
|
})
|
|
.collect(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl AsRefspecs for Refspec<git::PatternString, git::PatternString> {
|
|
fn as_refspecs(&self) -> Vec<Refspec<git::PatternString, git::PatternString>> {
|
|
vec![self.clone()]
|
|
}
|
|
|
|
fn into_refspecs(self) -> Vec<Refspec<git::PatternString, git::PatternString>> {
|
|
vec![self]
|
|
}
|
|
}
|
|
|
|
impl<T: AsRefspecs> AsRefspecs for Vec<T> {
|
|
fn as_refspecs(&self) -> Vec<Refspec<git::PatternString, git::PatternString>> {
|
|
self.iter().flat_map(AsRefspecs::as_refspecs).collect()
|
|
}
|
|
|
|
fn into_refspecs(self) -> Vec<Refspec<git::PatternString, git::PatternString>> {
|
|
self.into_iter()
|
|
.flat_map(AsRefspecs::into_refspecs)
|
|
.collect()
|
|
}
|
|
}
|
|
|
|
impl AsRefspecs for Remote {
|
|
fn as_refspecs(&self) -> Vec<Refspec<git::PatternString, git::PatternString>> {
|
|
let ns = self.id.to_namespace();
|
|
// Nb. the references in Refs are expected to be Qualified
|
|
self.refs
|
|
.iter()
|
|
.map(|(name, _)| {
|
|
let name = git::PatternString::from(ns.join(name));
|
|
Refspec {
|
|
src: name.clone(),
|
|
dst: name,
|
|
force: true,
|
|
}
|
|
})
|
|
.collect()
|
|
}
|
|
}
|
|
|
|
impl<'a> AsRefspecs for BTreeSet<git::Namespaced<'a>> {
|
|
fn as_refspecs(&self) -> Vec<Refspec<git::PatternString, git::PatternString>> {
|
|
let reserved = [(*IDENTITY_BRANCH).clone(), (*SIGREFS_BRANCH).clone()]
|
|
.into_iter()
|
|
.collect::<BTreeSet<_>>();
|
|
self.iter()
|
|
.map(|r| {
|
|
// Only force ordinary refs.
|
|
let suffix = r.strip_namespace();
|
|
let force = !reserved.contains(&suffix);
|
|
Refspec {
|
|
src: r.clone().to_ref_string().into(),
|
|
dst: r.clone().to_ref_string().into(),
|
|
force,
|
|
}
|
|
})
|
|
.collect()
|
|
}
|
|
}
|