diff --git a/node/src/git.rs b/node/src/git.rs index 37245cdf..dcfa7ab5 100644 --- a/node/src/git.rs +++ b/node/src/git.rs @@ -9,6 +9,7 @@ use crate::storage::refs::Refs; use crate::storage::RemoteId; pub use git_ext::Oid; +pub use git_ref_format::{refname, RefStr, RefString}; pub use git_url::Url; /// Default port of the `git` transport protocol. @@ -43,7 +44,7 @@ pub fn remote_refs(url: &Url) -> Result, ListRefsError> let (id, refname) = parse_ref::(r.name())?; let entry = remotes.entry(id).or_insert_with(Refs::default); - entry.insert(refname.to_string(), r.oid().into()); + entry.insert(refname, r.oid().into()); } Ok(remotes) diff --git a/node/src/storage.rs b/node/src/storage.rs index f62d77de..5e2c171b 100644 --- a/node/src/storage.rs +++ b/node/src/storage.rs @@ -15,8 +15,8 @@ pub use radicle_git_ext::Oid; use crate::collections::HashMap; use crate::crypto::{self, Unverified, Verified}; -use crate::git::RefError; use crate::git::Url; +use crate::git::{RefError, RefStr}; use crate::identity; use crate::identity::{ProjId, ProjIdError, UserId}; use crate::storage::refs::Refs; @@ -48,7 +48,6 @@ pub enum Error { } pub type RemoteId = UserId; -pub type RefName = String; /// Project remotes. Tracks the git state of a project. #[derive(Debug, Clone, PartialEq, Eq)] @@ -186,9 +185,9 @@ pub trait ReadRepository { fn reference( &self, user: &UserId, - reference: &str, + reference: &RefStr, ) -> Result, git2::Error>; - fn reference_oid(&self, user: &UserId, reference: &str) -> Result, git2::Error>; + fn reference_oid(&self, user: &UserId, reference: &RefStr) -> Result, git2::Error>; fn references(&self, user: &UserId) -> Result; fn remote(&self, user: &UserId) -> Result, refs::Error>; fn remotes(&self) -> Result, refs::Error>; diff --git a/node/src/storage/git.rs b/node/src/storage/git.rs index 957ab894..17106dcd 100644 --- a/node/src/storage/git.rs +++ b/node/src/storage/git.rs @@ -214,7 +214,7 @@ impl ReadRepository for Repository { fn reference( &self, remote: &RemoteId, - name: &str, + name: &git::RefStr, ) -> Result, git2::Error> { let name = format!("refs/remotes/{remote}/{name}"); self.backend.find_reference(&name).map(Some).or_else(|e| { @@ -226,7 +226,11 @@ impl ReadRepository for Repository { }) } - fn reference_oid(&self, user: &RemoteId, reference: &str) -> Result, git2::Error> { + fn reference_oid( + &self, + user: &RemoteId, + reference: &git::RefStr, + ) -> Result, git2::Error> { let reference = self.reference(user, reference)?; Ok(reference.and_then(|r| r.target().map(|o| o.into()))) } @@ -249,7 +253,7 @@ impl ReadRepository for Repository { let (_, refname) = git::parse_ref::(name)?; let oid = e.target().ok_or(Error::InvalidRef)?; - refs.insert(refname.to_string(), oid.into()); + refs.insert(refname, oid.into()); } Ok(refs.into()) } @@ -338,7 +342,7 @@ mod tests { // Strip the remote refs of sigrefs so we can compare them. for remote in refs.values_mut() { - remote.remove(SIGNATURE_REF).unwrap(); + remote.remove(&*SIGNATURE_REF).unwrap(); } assert_eq!(refs, remotes.into()); } @@ -351,7 +355,7 @@ mod tests { let inventory = alice.inventory().unwrap(); let proj = inventory.first().unwrap(); let remotes = alice.repository(proj).unwrap().remotes().unwrap(); - let refname = "heads/master"; + let refname = git::refname!("heads/master"); // Have Bob fetch Alice's refs. bob.repository(proj) @@ -366,10 +370,10 @@ mod tests { for (id, _) in remotes.into_iter() { let alice_repo = alice.repository(proj).unwrap(); - let alice_oid = alice_repo.reference(&id, refname).unwrap().unwrap(); + let alice_oid = alice_repo.reference(&id, &refname).unwrap().unwrap(); let bob_repo = bob.repository(proj).unwrap(); - let bob_oid = bob_repo.reference(&id, refname).unwrap().unwrap(); + let bob_oid = bob_repo.reference(&id, &refname).unwrap().unwrap(); assert_eq!(alice_oid.target(), bob_oid.target()); } @@ -403,7 +407,7 @@ mod tests { let mut unsigned = project.references(&alice).unwrap(); // The signed refs doesn't contain the signature ref itself. - unsigned.remove(SIGNATURE_REF).unwrap(); + unsigned.remove(&*SIGNATURE_REF).unwrap(); assert_eq!(remote.refs, signed); assert_eq!(*remote.refs, unsigned); diff --git a/node/src/storage/refs.rs b/node/src/storage/refs.rs index cd39b0a2..1ce5b8fa 100644 --- a/node/src/storage/refs.rs +++ b/node/src/storage/refs.rs @@ -7,6 +7,7 @@ use std::ops::{Deref, DerefMut}; use std::path::Path; use std::str::FromStr; +use once_cell::sync::Lazy; use radicle_git_ext as git_ext; use serde::{Deserialize, Serialize}; use thiserror::Error; @@ -18,7 +19,7 @@ use crate::git::Oid; use crate::storage; use crate::storage::{ReadRepository, RemoteId, WriteRepository}; -pub const SIGNATURE_REF: &str = "radicle/signature"; +pub static SIGNATURE_REF: Lazy = Lazy::new(|| git::refname!("radicle/signature")); pub const REFS_BLOB_PATH: &str = "refs"; pub const SIGNATURE_BLOB_PATH: &str = "signature"; @@ -51,7 +52,7 @@ pub enum Error { /// The published state of a local repository. #[derive(Default, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct Refs(BTreeMap); +pub struct Refs(BTreeMap); impl Refs { /// Verify the given signature on these refs, and return [`SignedRefs`] on success. @@ -100,7 +101,7 @@ impl Refs { .split_once(' ') .ok_or(canonical::Error::InvalidFormat)?; - let name = name.to_owned(); + let name = git::RefString::try_from(name)?; let oid = Oid::from_str(oid)?; refs.insert(name, oid); @@ -110,7 +111,7 @@ impl Refs { pub fn canonical(&self) -> Vec { let mut buf = String::new(); - let refs = self.iter().filter(|(name, _)| *name != SIGNATURE_REF); + let refs = self.iter().filter(|(name, _)| *name != &*SIGNATURE_REF); for (name, oid) in refs { buf.push_str(&oid.to_string()); @@ -122,7 +123,7 @@ impl Refs { } } -impl From for BTreeMap { +impl From for BTreeMap { fn from(refs: Refs) -> Self { refs.0 } @@ -134,14 +135,14 @@ impl From> for Refs { } } -impl From> for Refs { - fn from(refs: BTreeMap) -> Self { +impl From> for Refs { + fn from(refs: BTreeMap) -> Self { Self(refs) } } impl Deref for Refs { - type Target = BTreeMap; + type Target = BTreeMap; fn deref(&self) -> &Self::Target { &self.0 @@ -196,7 +197,7 @@ impl SignedRefs { where S: ReadRepository, { - if let Some(oid) = repo.reference_oid(remote, SIGNATURE_REF)? { + if let Some(oid) = repo.reference_oid(remote, &SIGNATURE_REF)? { Self::load_at(oid, remote, repo) } else { Err(Error::NotFound) @@ -233,8 +234,9 @@ impl SignedRefs { remote: &RemoteId, repo: &S, ) -> Result { + let sigref = &*SIGNATURE_REF; let parent: Option = repo - .reference(remote, SIGNATURE_REF)? + .reference(remote, sigref)? .map(|r| r.peel_to_commit()) .transpose()?; @@ -260,13 +262,13 @@ impl SignedRefs { } } - let sigref = format!("refs/remotes/{remote}/{SIGNATURE_REF}"); + let sigref = format!("refs/remotes/{remote}/{sigref}"); let author = repo.raw().signature()?; let commit = repo.raw().commit( Some(&sigref), &author, &author, - &format!("Update {} for {}", SIGNATURE_REF, remote), + &format!("Update {} for {}", sigref, remote), &tree, &parent.iter().collect::>(), ); @@ -306,6 +308,8 @@ pub mod canonical { #[derive(Debug, thiserror::Error)] pub enum Error { + #[error(transparent)] + InvalidRef(#[from] git_ref_format::Error), #[error("invalid canonical format")] InvalidFormat, #[error(transparent)] @@ -325,10 +329,6 @@ mod tests { let encoded = refs.canonical(); let decoded = Refs::from_canonical(&encoded).unwrap(); - println!("{:?}", refs); - assert_eq!(refs, decoded); } } - -// TODO: Test canonical/from_canonical diff --git a/node/src/test/arbitrary.rs b/node/src/test/arbitrary.rs index 9c1ea01e..beed679d 100644 --- a/node/src/test/arbitrary.rs +++ b/node/src/test/arbitrary.rs @@ -7,6 +7,7 @@ use quickcheck::Arbitrary; use crate::collections::HashMap; use crate::crypto::{self, Signer}; use crate::crypto::{PublicKey, SecretKey}; +use crate::git; use crate::hash; use crate::identity::ProjId; use crate::storage; @@ -50,7 +51,7 @@ impl Arbitrary for MockStorage { impl Arbitrary for Refs { fn arbitrary(g: &mut quickcheck::Gen) -> Self { - let mut refs: BTreeMap = BTreeMap::new(); + let mut refs: BTreeMap = BTreeMap::new(); let mut bytes: [u8; 20] = [0; 20]; let names = &[ "heads/master", @@ -69,7 +70,9 @@ impl Arbitrary for Refs { *byte = u8::arbitrary(g); } let oid = storage::Oid::try_from(&bytes[..]).unwrap(); - refs.insert(name.to_string(), oid); + let name = git::RefString::try_from(*name).unwrap(); + + refs.insert(name, oid); } } Self::from(refs) diff --git a/node/src/test/storage.rs b/node/src/test/storage.rs index c3b9e184..f3bb6f86 100644 --- a/node/src/test/storage.rs +++ b/node/src/test/storage.rs @@ -1,6 +1,7 @@ use git_url::Url; use crate::crypto::Verified; +use crate::git; use crate::identity::{ProjId, UserId}; use crate::storage::refs; use crate::storage::{ @@ -96,7 +97,7 @@ impl ReadRepository for MockRepository { fn reference( &self, _user: &UserId, - _reference: &str, + _reference: &git::RefStr, ) -> Result, git2::Error> { todo!() } @@ -104,7 +105,7 @@ impl ReadRepository for MockRepository { fn reference_oid( &self, _user: &UserId, - _reference: &str, + _reference: &git::RefStr, ) -> Result, git2::Error> { todo!() }