238 lines · 7.4 KB · Rust
RawDownload
use std::{borrow::Borrow, convert::TryFrom, path::Path}; use git_object::bstr::{BStr, BString, ByteSlice}; use crate::{bstr::ByteVec, name::is_pseudo_ref, Category, FullName, FullNameRef, Namespace, PartialNameRef}; impl TryFrom<&str> for FullName {    type Error = git_validate::refname::Error;     fn try_from(value: &str) -> Result<Self, Self::Error> {        Ok(FullName(git_validate::refname(value.as_bytes().as_bstr())?.into()))    }} impl TryFrom<String> for FullName {    type Error = git_validate::refname::Error;     fn try_from(value: String) -> Result<Self, Self::Error> {        git_validate::refname(value.as_bytes().as_bstr())?;        Ok(FullName(value.into()))    }} impl TryFrom<&BStr> for FullName {    type Error = git_validate::refname::Error;     fn try_from(value: &BStr) -> Result<Self, Self::Error> {        Ok(FullName(git_validate::refname(value)?.into()))    }} impl TryFrom<BString> for FullName {    type Error = git_validate::refname::Error;     fn try_from(value: BString) -> Result<Self, Self::Error> {        git_validate::refname(value.as_ref())?;        Ok(FullName(value))    }} impl TryFrom<&BString> for FullName {    type Error = git_validate::refname::Error;     fn try_from(value: &BString) -> Result<Self, Self::Error> {        git_validate::refname(value.as_ref())?;        Ok(FullName(value.clone()))    }} impl From<FullName> for BString {    fn from(name: FullName) -> Self {        name.0    }} impl<'a> From<&'a FullNameRef> for &'a BStr {    fn from(name: &'a FullNameRef) -> Self {        &name.0    }} impl<'a> From<&'a FullNameRef> for FullName {    fn from(value: &'a FullNameRef) -> Self {        FullName(value.as_bstr().into())    }} impl std::fmt::Display for FullName {    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {        std::fmt::Display::fmt(&self.0, f)    }} impl FullNameRef {    /// Interpret this fully qualified reference name as partial name.    pub fn as_partial_name(&self) -> &PartialNameRef {        PartialNameRef::new_unchecked(self.0.as_bstr())    }     /// Convert this name into the relative path identifying the reference location.    pub fn to_path(&self) -> &Path {        git_path::from_byte_slice(&self.0)    }     /// Return ourselves as byte string which is a valid refname    pub fn as_bstr(&self) -> &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) -> &BStr {        self.category_and_short_name()            .map(|(_, short)| short)            .unwrap_or_else(|| self.0.as_bstr())    }     /// Classify this name, or return `None` if it's unclassified.    pub fn category(&self) -> Option<Category<'_>> {        self.category_and_short_name().map(|(cat, _)| cat)    }     /// Classify this name, or return `None` if it's unclassified. If `Some`,    /// the shortened name is returned as well.    pub fn category_and_short_name(&self) -> Option<(Category<'_>, &BStr)> {        let name = self.0.as_bstr();        for category in &[Category::Tag, Category::LocalBranch, Category::RemoteBranch] {            if let Some(shortened) = name.strip_prefix(category.prefix().as_ref()) {                return Some((*category, shortened.as_bstr()));            }        }         for category in &[            Category::Note,            Category::Bisect,            Category::WorktreePrivate,            Category::Rewritten,        ] {            if name.starts_with(category.prefix().as_ref()) {                return Some((                    *category,                    name.strip_prefix(b"refs/")                        .expect("we checked for refs/* above")                        .as_bstr(),                ));            }        }         if is_pseudo_ref(name) {            Some((Category::PseudoRef, name))        } else if let Some(shortened) = name.strip_prefix(Category::MainPseudoRef.prefix().as_ref()) {            if shortened.starts_with_str("refs/") {                (Category::MainRef, shortened.as_bstr()).into()            } else {                is_pseudo_ref(shortened).then(|| (Category::MainPseudoRef, shortened.as_bstr()))            }        } else if let Some(shortened_with_worktree_name) =            name.strip_prefix(Category::LinkedPseudoRef { name: "".into() }.prefix().as_ref())        {            let (name, shortened) = shortened_with_worktree_name.find_byte(b'/').map(|pos| {                (                    shortened_with_worktree_name[..pos].as_bstr(),                    shortened_with_worktree_name[pos + 1..].as_bstr(),                )            })?;            if shortened.starts_with_str("refs/") {                (Category::LinkedRef { name }, shortened.as_bstr()).into()            } else {                is_pseudo_ref(shortened).then(|| (Category::LinkedPseudoRef { name }, shortened.as_bstr()))            }        } else {            None        }    }} impl FullName {    /// Convert this name into the relative path, lossily, identifying the reference location relative to a repository    pub fn to_path(&self) -> &Path {        git_path::from_byte_slice(&self.0)    }     /// Dissolve this instance and return the buffer.    pub fn into_inner(self) -> BString {        self.0    }     /// Return ourselves as byte string which is a valid refname    pub fn as_bstr(&self) -> &BStr {        self.0.as_bstr()    }     /// Modify ourself so that we use `namespace` as prefix, if it is not yet in the `namespace`    pub fn prefix_namespace(&mut self, namespace: &Namespace) -> &mut Self {        if !self.0.starts_with_str(&namespace.0) {            self.0.insert_str(0, &namespace.0);        }        self    }     /// Strip the given `namespace` off the beginning of this name, if it is in this namespace.    pub fn strip_namespace(&mut self, namespace: &Namespace) -> &mut Self {        if self.0.starts_with_str(&namespace.0) {            let prev_len = self.0.len();            self.0.copy_within(namespace.0.len().., 0);            self.0.resize(prev_len - namespace.0.len(), 0);        }        self    }     /// 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) -> &BStr {        self.as_ref().shorten()    }     /// Classify this name, or return `None` if it's unclassified.    pub fn category(&self) -> Option<crate::Category<'_>> {        self.as_ref().category()    }     /// Classify this name, or return `None` if it's unclassified. If `Some`,    /// the shortened name is returned as well.    pub fn category_and_short_name(&self) -> Option<(crate::Category<'_>, &BStr)> {        self.as_ref().category_and_short_name()    }} impl FullNameRef {    /// Return the file name portion of a full name, for instance `main` if the    /// full name was `refs/heads/main`.    pub fn file_name(&self) -> &BStr {        self.0.rsplitn(2, |b| *b == b'/').next().expect("valid ref").as_bstr()    }} impl Borrow<FullNameRef> for FullName {    #[inline]    fn borrow(&self) -> &FullNameRef {        FullNameRef::new_unchecked(self.0.as_bstr())    }} impl AsRef<FullNameRef> for FullName {    fn as_ref(&self) -> &FullNameRef {        self.borrow()    }} impl ToOwned for FullNameRef {    type Owned = FullName;     fn to_owned(&self) -> Self::Owned {        FullName(self.0.to_owned())    }}