| + |
//! Git reference names, namespaced and not.
|
| + |
|
| + |
use radicle::git::{BranchName, Component, Namespaced, Qualified, RefStr, RefString};
|
| + |
|
| + |
/// Convert a plain branch name (`main`) from a Git ref
|
| + |
pub fn branch_ref(name: &RefStr) -> Result<BranchName, RefError> {
|
| + |
if name.starts_with("refs/") {
|
| + |
return Err(RefError::RefsInName(name.to_ref_string()));
|
| + |
}
|
| + |
ref_string(name)
|
| + |
}
|
| + |
|
| + |
/// Create a [`BranchName`] from a string slice.
|
| + |
pub fn branch_from_str(s: &str) -> Result<BranchName, RefError> {
|
| + |
branch_ref(&ref_string(s)?)
|
| + |
}
|
| + |
|
| + |
/// Convert a branch name to a [`Qualified`]: `refs/heads/main`.
|
| + |
pub fn qualified_branch(name: &BranchName) -> Result<Qualified, RefError> {
|
| + |
if name.starts_with("refs/") {
|
| + |
return Err(RefError::RefsInName(name.to_ref_string()));
|
| + |
}
|
| + |
let qualified_name = ref_string(&format!("refs/heads/{name}"))?;
|
| + |
let x = Qualified::from_refstr(qualified_name);
|
| + |
x.ok_or(RefError::QualifiedCreate(name.clone()))
|
| + |
}
|
| + |
|
| + |
/// Create a [`RefString`] from a [`String`].
|
| + |
pub fn ref_string(s: &str) -> Result<RefString, RefError> {
|
| + |
RefString::try_from(s).map_err(|err| RefError::RefStrCreate(s.into(), err))
|
| + |
}
|
| + |
|
| + |
/// Create a name spaced branch name.
|
| + |
pub fn namespaced_branch<'a>(
|
| + |
ns: &RefStr,
|
| + |
branch: &'a BranchName,
|
| + |
) -> Result<Namespaced<'a>, RefError> {
|
| + |
let ns = Component::from_refstr(ns).ok_or(RefError::NamespaceName(ns.to_ref_string()))?;
|
| + |
Ok(qualified_branch(branch)?.with_namespace(ns))
|
| + |
}
|
| + |
|
| + |
/// Create a name spaced ref name from a string slice.
|
| + |
pub fn namespaced_from_str(s: &str) -> Result<Namespaced, RefError> {
|
| + |
assert!(s.starts_with("refs/namespaces/"));
|
| + |
let rs = ref_string(s)?;
|
| + |
Ok(rs
|
| + |
.to_namespaced()
|
| + |
.ok_or(RefError::NamespacedCreate(s.into()))?
|
| + |
.to_owned())
|
| + |
}
|
| + |
|
| + |
/// Extract a [`BranchName`] from a name spaced branch.
|
| + |
pub fn branch_from_namespaced(ns: &Namespaced) -> Result<BranchName, RefError> {
|
| + |
let plain_ref = ns.strip_namespace();
|
| + |
|
| + |
let (refs, heads, first, rest) = plain_ref.non_empty_components();
|
| + |
if refs.as_str() != "refs" || heads.as_str() != "heads" {
|
| + |
return Err(RefError::NotABranch(ns.to_ref_string()));
|
| + |
}
|
| + |
Ok(BranchName::from_iter(
|
| + |
[first.into_inner().to_ref_string()]
|
| + |
.iter()
|
| + |
.cloned()
|
| + |
.chain(rest.map(|c| c.into_inner().to_ref_string())),
|
| + |
))
|
| + |
}
|
| + |
|
| + |
/// All errors from Git reference manipulation.
|
| + |
#[derive(Debug, thiserror::Error, Eq, PartialEq)]
|
| + |
pub enum RefError {
|
| + |
/// Branch name starts with "refs/", but a plain name is wanted.
|
| + |
#[error("programming error: plain branch name must not start with refs/: {0:?}")]
|
| + |
RefsInName(RefString),
|
| + |
|
| + |
/// Failed to create a [`RefString`] from a string.
|
| + |
#[error("failed to create a RefStr from a string {0:?}")]
|
| + |
RefStrCreate(String, #[source] radicle::git::fmt::Error),
|
| + |
|
| + |
/// Can't create a [`Qualified`] value from a [`BranchName`].
|
| + |
#[error("failed to create a qualified branch name from a branch ref {0:?}")]
|
| + |
QualifiedCreate(BranchName),
|
| + |
|
| + |
/// Can't create a [`Namespaced`] value from a string slice.
|
| + |
#[error("failed to create a name spaced Git ref from {0:?}")]
|
| + |
NamespacedCreate(String),
|
| + |
|
| + |
/// Name spaced branch name does not start with `refs/heads`".
|
| + |
#[error("failed to get branch name from {0:?})")]
|
| + |
NotABranch(RefString),
|
| + |
|
| + |
/// Can't create a [`Component`] from a name space name.
|
| + |
#[error("failed to create a name space component from its name {0:?}")]
|
| + |
NamespaceName(RefString),
|
| + |
}
|
| + |
|
| + |
#[cfg(test)]
|
| + |
#[allow(clippy::unwrap_used)]
|
| + |
mod test {
|
| + |
use super::*;
|
| + |
|
| + |
#[test]
|
| + |
fn ref_string_from_plain_branch_name() {
|
| + |
assert_eq!(ref_string("main").map(|x| x.to_string()), Ok("main".into()));
|
| + |
}
|
| + |
|
| + |
#[test]
|
| + |
fn plain_branch_name() {
|
| + |
assert_eq!(
|
| + |
branch_ref(&ref_string("main").unwrap()).map(|x| x.to_string()),
|
| + |
Ok("main".into())
|
| + |
);
|
| + |
}
|
| + |
|
| + |
#[test]
|
| + |
fn qualified_branch_name_from_plain() {
|
| + |
let name = branch_ref(&ref_string("main").unwrap()).unwrap();
|
| + |
assert_eq!(
|
| + |
qualified_branch(&name).map(|x| x.to_string()),
|
| + |
Ok("refs/heads/main".into())
|
| + |
);
|
| + |
}
|
| + |
|
| + |
#[test]
|
| + |
fn namespaced_branch_from_plain() {
|
| + |
let branch = branch_ref(&ref_string("main").unwrap()).unwrap();
|
| + |
let ns = ref_string("node1").unwrap();
|
| + |
let name = namespaced_branch(&ns, &branch);
|
| + |
assert_eq!(
|
| + |
name.map(|x| x.to_string()),
|
| + |
Ok("refs/namespaces/node1/refs/heads/main".into())
|
| + |
);
|
| + |
}
|
| + |
|
| + |
#[test]
|
| + |
fn extracts_branch_namespaced_branch() {
|
| + |
let branch = branch_ref(&ref_string("main").unwrap()).unwrap();
|
| + |
let ns = ref_string("node1").unwrap();
|
| + |
let name = namespaced_branch(&ns, &branch).unwrap();
|
| + |
let extracted = branch_from_namespaced(&name);
|
| + |
assert_eq!(extracted.map(|x| x.to_string()), Ok("main".into()));
|
| + |
}
|
| + |
}
|