radicle_surf/
namespace.rs1use std::{
2 convert::TryFrom,
3 fmt,
4 str::{self, FromStr},
5};
6
7use git_ext::ref_format::{
8 self,
9 refspec::{NamespacedPattern, PatternString, QualifiedPattern},
10 Component, Namespaced, Qualified, RefStr, RefString,
11};
12use nonempty::NonEmpty;
13use thiserror::Error;
14
15#[derive(Debug, Error)]
16pub enum Error {
17 #[error("namespaces must not be empty")]
20 EmptyNamespace,
21 #[error(transparent)]
22 RefFormat(#[from] ref_format::Error),
23 #[error(transparent)]
24 Utf8(#[from] str::Utf8Error),
25}
26
27#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
37pub struct Namespace {
38 pub(super) namespaces: RefString,
41}
42
43impl Namespace {
44 pub(crate) fn to_namespaced<'a>(&self, name: &Qualified<'a>) -> Namespaced<'a> {
58 let mut components = self.namespaces.components().rev();
59 let mut namespaced = name.with_namespace(
60 components
61 .next()
62 .expect("BUG: 'namespaces' cannot be empty"),
63 );
64 for ns in components {
65 let qualified = namespaced.into_qualified();
66 namespaced = qualified.with_namespace(ns);
67 }
68 namespaced
69 }
70
71 pub(crate) fn to_namespaced_pattern<'a>(
85 &self,
86 pat: &QualifiedPattern<'a>,
87 ) -> NamespacedPattern<'a> {
88 let pattern = PatternString::from(self.namespaces.clone());
89 let mut components = pattern.components().rev();
90 let mut namespaced = pat
91 .with_namespace(
92 components
93 .next()
94 .expect("BUG: 'namespaces' cannot be empty"),
95 )
96 .expect("BUG: 'namespace' cannot have globs");
97 for ns in components {
98 let qualified = namespaced.into_qualified();
99 namespaced = qualified
100 .with_namespace(ns)
101 .expect("BUG: 'namespaces' cannot have globs");
102 }
103 namespaced
104 }
105}
106
107impl fmt::Display for Namespace {
108 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109 write!(f, "{}", self.namespaces)
110 }
111}
112
113impl<'a> From<NonEmpty<Component<'a>>> for Namespace {
114 fn from(cs: NonEmpty<Component<'a>>) -> Self {
115 Self {
116 namespaces: cs.into_iter().collect::<RefString>(),
117 }
118 }
119}
120
121impl TryFrom<&str> for Namespace {
122 type Error = Error;
123
124 fn try_from(name: &str) -> Result<Self, Self::Error> {
125 Self::from_str(name)
126 }
127}
128
129impl TryFrom<&[u8]> for Namespace {
130 type Error = Error;
131
132 fn try_from(namespace: &[u8]) -> Result<Self, Self::Error> {
133 str::from_utf8(namespace)
134 .map_err(Error::from)
135 .and_then(Self::from_str)
136 }
137}
138
139impl FromStr for Namespace {
140 type Err = Error;
141
142 fn from_str(name: &str) -> Result<Self, Self::Err> {
143 let namespaces = RefStr::try_from_str(name)?.to_ref_string();
144 Ok(Self { namespaces })
145 }
146}
147
148impl From<Namespaced<'_>> for Namespace {
149 fn from(namespaced: Namespaced<'_>) -> Self {
150 let mut namespaces = namespaced.namespace().to_ref_string();
151 let mut qualified = namespaced.strip_namespace();
152 while let Some(namespaced) = qualified.to_namespaced() {
153 namespaces.push(namespaced.namespace());
154 qualified = namespaced.strip_namespace();
155 }
156 Self { namespaces }
157 }
158}
159
160impl TryFrom<&git2::Reference<'_>> for Namespace {
161 type Error = Error;
162
163 fn try_from(reference: &git2::Reference) -> Result<Self, Self::Error> {
164 let name = RefStr::try_from_str(str::from_utf8(reference.name_bytes())?)?;
165 name.to_namespaced()
166 .ok_or(Error::EmptyNamespace)
167 .map(Self::from)
168 }
169}