use std::{borrow::Cow, convert::TryFrom}; use super::Name; use crate::bstr::{BStr, BString, ByteSlice, ByteVec}; /// The error returned by [validated()]. #[derive(Debug, thiserror::Error)] #[error("remote names must be valid within refspecs for fetching: {name:?}")] #[allow(missing_docs)] pub struct Error { pub source: gix_refspec::parse::Error, pub name: BString, } /// Return `name` if it is valid as symbolic remote name. /// /// This means it has to be valid within a the ref path of a tracking branch. pub fn validated(name: impl Into) -> Result { let name = name.into(); match gix_refspec::parse( format!("refs/heads/test:refs/remotes/{name}/test").as_str().into(), gix_refspec::parse::Operation::Fetch, ) { Ok(_) => Ok(name), Err(err) => Err(Error { source: err, name }), } } impl Name<'_> { /// Obtain the name as string representation. pub fn as_bstr(&self) -> &BStr { match self { Name::Symbol(v) => v.as_ref().into(), Name::Url(v) => v.as_ref(), } } /// Return this instance as a symbolic name, if it is one. pub fn as_symbol(&self) -> Option<&str> { match self { Name::Symbol(n) => n.as_ref().into(), Name::Url(_) => None, } } /// Return this instance as url, if it is one. pub fn as_url(&self) -> Option<&BStr> { match self { Name::Url(n) => n.as_ref().into(), Name::Symbol(_) => None, } } } impl<'a> TryFrom> for Name<'a> { type Error = Cow<'a, BStr>; fn try_from(name: Cow<'a, BStr>) -> Result { if name.contains(&b'/') || name.as_ref() == "." { Ok(Name::Url(name)) } else { match name { Cow::Borrowed(n) => n.to_str().ok().map(Cow::Borrowed).ok_or(name), Cow::Owned(n) => Vec::from(n) .into_string() .map_err(|err| Cow::Owned(err.into_vec().into())) .map(Cow::Owned), } .map(Name::Symbol) } } } impl From for Name<'static> { fn from(name: BString) -> Self { Self::try_from(Cow::Owned(name)).expect("String is never illformed") } } impl<'a> AsRef for Name<'a> { fn as_ref(&self) -> &BStr { self.as_bstr() } }