diff --git a/git-ref/src/namespace.rs b/git-ref/src/namespace.rs index 63f30204c45..f952107c421 100644 --- a/git-ref/src/namespace.rs +++ b/git-ref/src/namespace.rs @@ -1,6 +1,7 @@ #![allow(missing_docs)] use crate::PartialName; +use bstr::{BString, ByteSlice, ByteVec}; use std::convert::TryInto; pub fn expand<'a, Name, E>(namespace: Name) -> Result @@ -8,8 +9,19 @@ where Name: TryInto, Error = E>, expand::Error: From, { - let _namespace = namespace.try_into()?; - todo!("impl") + let namespace = namespace.try_into()?.0; + let mut out = BString::default(); + for component in namespace.split_str(b"/") { + out.push_str("refs/namespaces/"); + out.push_str(component); + out.push_str(b"/"); + } + out.pop(); + debug_assert!( + git_validate::reference::name(out.as_ref()).is_ok(), + "we always produce valid ref names" + ); + Ok(crate::mutable::FullName(out)) } pub mod expand { diff --git a/git-ref/tests/namespace/mod.rs b/git-ref/tests/namespace/mod.rs index ae8cfaf8967..54180d7309a 100644 --- a/git-ref/tests/namespace/mod.rs +++ b/git-ref/tests/namespace/mod.rs @@ -1,19 +1,31 @@ -mod expand { +od expand { #[test] fn each_component_expands_to_the_namespace_prefix_individually() { assert_eq!( git_ref::namespace::expand("foo/bar").unwrap().as_bstr(), - "refs/namespaces/foo/refs/namespaces/bar/" + "refs/namespaces/foo/refs/namespaces/bar" ) } #[test] - #[ignore] - fn only_backslashes_are_valid_component_separators() {} + fn backslashes_are_no_component_separators_and_invalid() { + assert!(matches!( + git_ref::namespace::expand("foo\\bar").expect_err("empty invalid"), + git_ref::namespace::expand::Error::RefnameValidation(git_validate::refname::Error::Tag( + git_validate::tag::name::Error::InvalidByte(byte) + ))if byte == "\\" + )); + } #[test] - #[ignore] - fn trailing_slashes_do_nothing() {} + fn trailing_slashes_are_not_allowed() { + assert!(matches!( + git_ref::namespace::expand("foo/").expect_err("empty invalid"), + git_ref::namespace::expand::Error::RefnameValidation(git_validate::refname::Error::Tag( + git_validate::tag::name::Error::EndsWithSlash + )) + )); + } #[test] fn empty_namespaces_are_not_allowed() { @@ -24,4 +36,20 @@ mod expand { )) )); } + #[test] + fn bare_slashes_are_not_allowed() { + assert!(matches!( + git_ref::namespace::expand("/").expect_err("empty invalid"), + git_ref::namespace::expand::Error::RefnameValidation(git_validate::refname::Error::Tag( + git_validate::tag::name::Error::EndsWithSlash + )) + )); + } + #[test] + fn repeated_slashes_are_invalid() { + assert!(matches!( + git_ref::namespace::expand("foo//bar").expect_err("empty invalid"), + git_ref::namespace::expand::Error::RefnameValidation(git_validate::refname::Error::RepeatedSlash) + )); + } } diff --git a/git-validate/src/tag.rs b/git-validate/src/tag.rs index 9143c2d295e..46004ec8357 100644 --- a/git-validate/src/tag.rs +++ b/git-validate/src/tag.rs @@ -51,7 +51,7 @@ pub fn name(bytes: &BStr) -> Result<&BStr, name::Error> { for byte in bytes.iter() { match byte { b'\\' | b'^' | b':' | b'[' | b'?' | b' ' | b'~' | b'\0'..=b'\x1F' | b'\x7F' => { - return Err(name::Error::InvalidByte(bytes.into())) + return Err(name::Error::InvalidByte((&[*byte][..]).into())) } b'*' => return Err(name::Error::Asterisk), b'.' if previous == b'.' => return Err(name::Error::DoubleDot),