use std::{ convert::{Infallible, TryFrom}, ffi::OsStr, path::Path, }; use git_object::bstr::{BStr, BString, ByteSlice, ByteVec}; use crate::{Category, FullNameRef, PartialNameRef}; /// The error used in the [`PartialNameRef`][super::PartialNameRef]::try_from(…) implementations. pub type Error = git_validate::reference::name::Error; impl Category { /// Return the prefix that would contain all references of our kind. pub fn prefix(&self) -> &BStr { match self { Category::Tag => b"refs/tags/".as_bstr(), Category::LocalBranch => b"refs/heads/".as_bstr(), Category::RemoteBranch => b"refs/remotes/".as_bstr(), Category::Note => b"refs/notes/".as_bstr(), } } } impl<'a> FullNameRef<'a> { /// Convert this name into the relative path identifying the reference location. pub fn to_path(self) -> &'a Path { git_features::path::from_byte_slice_or_panic_on_windows(self.0) } /// Return ourselves as byte string which is a valid refname pub fn as_bstr(&self) -> &'a BStr { self.0 } /// Strip well-known prefixes from the name and return it. /// /// If there is no such prefix, the original name is returned. pub fn shorten(&self) -> &'a BStr { let n = self.as_bstr(); n.strip_prefix(b"refs/tags/") .or_else(|| n.strip_prefix(b"refs/heads/")) .or_else(|| n.strip_prefix(b"refs/remotes/")) .or_else(|| n.strip_prefix(b"refs/")) .map(|n| n.as_bstr()) .unwrap_or(n) } /// Classify this name, or return `None` if it's unclassified. pub fn category(&self) -> Option { for kind in &[ Category::Tag, Category::LocalBranch, Category::RemoteBranch, Category::Note, ] { if self.0.starts_with(kind.prefix()) { return (*kind).into(); } } None } } impl<'a> PartialNameRef<'a> { /// Convert this name into the relative path possibly identifying the reference location. /// Note that it may be only a partial path though. pub fn to_partial_path(&'a self) -> &'a Path { git_features::path::from_byte_slice_or_panic_on_windows(self.0.as_ref()) } /// Provide the name as binary string which is known to be a valid partial ref name. pub fn as_bstr(&'a self) -> &'a BStr { self.0.as_ref() } } impl PartialNameRef<'static> { /// Append the `component` to ourselves and validate the newly created partial path. /// /// Note that this method is meant to have an owned starting point as this is considered /// the typical usecase. pub fn join(self, component: impl AsRef<[u8]>) -> Result { let mut b = self.0.into_owned(); b.push_byte(b'/'); b.extend(component.as_ref()); git_validate::reference::name_partial(b.as_ref())?; Ok(PartialNameRef(b.into())) } } impl<'a> TryFrom<&'a BStr> for FullNameRef<'a> { type Error = Error; fn try_from(v: &'a BStr) -> Result { Ok(FullNameRef(git_validate::reference::name(v)?)) } } impl<'a> TryFrom> for PartialNameRef<'a> { type Error = Infallible; fn try_from(v: FullNameRef<'a>) -> Result { Ok(PartialNameRef(v.0.into())) } } impl<'a> TryFrom<&'a OsStr> for PartialNameRef<'a> { type Error = Error; fn try_from(v: &'a OsStr) -> Result { let v = git_features::path::os_str_into_bytes(v) .map_err(|_| Error::Tag(git_validate::tag::name::Error::InvalidByte("".into())))?; Ok(PartialNameRef( git_validate::reference::name_partial(v.as_bstr())?.into(), )) } } impl<'a> TryFrom<&'a BStr> for PartialNameRef<'a> { type Error = Error; fn try_from(v: &'a BStr) -> Result { Ok(PartialNameRef(git_validate::reference::name_partial(v)?.into())) } } impl<'a> TryFrom<&'a str> for FullNameRef<'a> { type Error = Error; fn try_from(v: &'a str) -> Result { let v = v.as_bytes().as_bstr(); Ok(FullNameRef(git_validate::reference::name(v)?)) } } impl<'a> TryFrom<&'a str> for PartialNameRef<'a> { type Error = Error; fn try_from(v: &'a str) -> Result { let v = v.as_bytes().as_bstr(); Ok(PartialNameRef(git_validate::reference::name_partial(v)?.into())) } } impl<'a> TryFrom<&'a String> for FullNameRef<'a> { type Error = Error; fn try_from(v: &'a String) -> Result { let v = v.as_bytes().as_bstr(); Ok(FullNameRef(git_validate::reference::name(v)?)) } } impl<'a> TryFrom<&'a String> for PartialNameRef<'a> { type Error = Error; fn try_from(v: &'a String) -> Result { let v = v.as_bytes().as_bstr(); Ok(PartialNameRef(git_validate::reference::name_partial(v)?.into())) } } impl TryFrom for PartialNameRef<'static> { type Error = Error; fn try_from(v: String) -> Result { git_validate::reference::name_partial(v.as_bytes().as_bstr())?; Ok(PartialNameRef(BString::from(v).into())) } } impl TryFrom for PartialNameRef<'static> { type Error = Error; fn try_from(v: BString) -> Result { git_validate::reference::name_partial(v.as_ref())?; Ok(PartialNameRef(v.into())) } }