1use std::{
2 borrow::Cow,
3 fmt::{self, Display},
4 ops::Deref,
5};
6
7use crate::{
8 lit, name,
9 refspec::{PatternStr, QualifiedPattern},
10 Component, RefStr, RefString,
11};
12
13#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]
19pub struct Qualified<'a>(pub(crate) Cow<'a, RefStr>);
20
21impl<'a> Qualified<'a> {
22 pub fn from_components<'b, 'c, 'd, A, B, C>(a: A, b: B, tail: C) -> Self
40 where
41 A: Into<Component<'b>>,
42 B: Into<Component<'c>>,
43 C: IntoIterator<Item = Component<'d>>,
44 {
45 let mut inner = name::REFS.join(a.into()).and(b.into());
46 inner.extend(tail);
47
48 Self(inner.into())
49 }
50
51 pub fn from_refstr(r: impl Into<Cow<'a, RefStr>>) -> Option<Self> {
52 Self::_from_refstr(r.into())
53 }
54
55 fn _from_refstr(r: Cow<'a, RefStr>) -> Option<Self> {
56 let mut iter = r.iter();
57 match (iter.next()?, iter.next()?, iter.next()?) {
58 ("refs", _, _) => Some(Qualified(r)),
59 _ => None,
60 }
61 }
62
63 #[inline]
64 pub fn as_str(&self) -> &str {
65 self.as_ref()
66 }
67
68 #[inline]
69 pub fn join<'b, R>(&self, other: R) -> Qualified<'b>
70 where
71 R: AsRef<RefStr>,
72 {
73 Qualified(self.0.join(other).into())
74 }
75
76 pub fn to_pattern<P>(&'a self, pattern: P) -> QualifiedPattern<'a>
77 where
78 P: AsRef<PatternStr>,
79 {
80 QualifiedPattern(Cow::Owned(RefStr::to_pattern(self, pattern.as_ref())))
81 }
82
83 #[inline]
84 pub fn to_namespaced(&'a self) -> Option<Namespaced<'a>> {
85 self.0.as_ref().into()
86 }
87
88 pub fn with_namespace<'b>(&self, ns: Component<'b>) -> Namespaced<'a> {
93 Namespaced(Cow::Owned(
94 IntoIterator::into_iter([lit::Refs.into(), lit::Namespaces.into(), ns])
95 .chain(self.0.components())
96 .collect(),
97 ))
98 }
99
100 pub fn non_empty_iter(&'a self) -> (&'a str, &'a str, &'a str, name::Iter<'a>) {
102 let mut iter = self.iter();
103 (
104 iter.next().unwrap(),
105 iter.next().unwrap(),
106 iter.next().unwrap(),
107 iter,
108 )
109 }
110
111 pub fn non_empty_components(
118 &'a self,
119 ) -> (
120 Component<'a>,
121 Component<'a>,
122 Component<'a>,
123 name::Components<'a>,
124 ) {
125 let mut cs = self.components();
126 (
127 cs.next().unwrap(),
128 cs.next().unwrap(),
129 cs.next().unwrap(),
130 cs,
131 )
132 }
133
134 #[inline]
135 pub fn to_owned<'b>(&self) -> Qualified<'b> {
136 Qualified(Cow::Owned(self.0.clone().into_owned()))
137 }
138
139 #[inline]
140 pub fn into_owned<'b>(self) -> Qualified<'b> {
141 Qualified(Cow::Owned(self.0.into_owned()))
142 }
143
144 #[inline]
145 pub fn into_refstring(self) -> RefString {
146 self.into()
147 }
148}
149
150impl Deref for Qualified<'_> {
151 type Target = RefStr;
152
153 #[inline]
154 fn deref(&self) -> &Self::Target {
155 &self.0
156 }
157}
158
159impl AsRef<RefStr> for Qualified<'_> {
160 #[inline]
161 fn as_ref(&self) -> &RefStr {
162 self
163 }
164}
165
166impl AsRef<str> for Qualified<'_> {
167 #[inline]
168 fn as_ref(&self) -> &str {
169 self.0.as_str()
170 }
171}
172
173impl AsRef<Self> for Qualified<'_> {
174 #[inline]
175 fn as_ref(&self) -> &Self {
176 self
177 }
178}
179
180impl<'a> From<Qualified<'a>> for Cow<'a, RefStr> {
181 #[inline]
182 fn from(q: Qualified<'a>) -> Self {
183 q.0
184 }
185}
186
187impl From<Qualified<'_>> for RefString {
188 #[inline]
189 fn from(q: Qualified) -> Self {
190 q.0.into_owned()
191 }
192}
193
194impl<T, U> From<(lit::Refs, T, U)> for Qualified<'_>
195where
196 T: AsRef<RefStr>,
197 U: AsRef<RefStr>,
198{
199 #[inline]
200 fn from((refs, cat, name): (lit::Refs, T, U)) -> Self {
201 let refs: &RefStr = refs.into();
202 Self(Cow::Owned(refs.join(cat).and(name)))
203 }
204}
205
206impl<T> From<lit::RefsHeads<T>> for Qualified<'_>
207where
208 T: AsRef<RefStr>,
209{
210 #[inline]
211 fn from((refs, heads, name): lit::RefsHeads<T>) -> Self {
212 Self(Cow::Owned(
213 IntoIterator::into_iter([Component::from(refs), heads.into()])
214 .collect::<RefString>()
215 .and(name),
216 ))
217 }
218}
219
220impl<T> From<lit::RefsTags<T>> for Qualified<'_>
221where
222 T: AsRef<RefStr>,
223{
224 #[inline]
225 fn from((refs, tags, name): lit::RefsTags<T>) -> Self {
226 Self(Cow::Owned(
227 IntoIterator::into_iter([Component::from(refs), tags.into()])
228 .collect::<RefString>()
229 .and(name),
230 ))
231 }
232}
233
234impl<T> From<lit::RefsNotes<T>> for Qualified<'_>
235where
236 T: AsRef<RefStr>,
237{
238 #[inline]
239 fn from((refs, notes, name): lit::RefsNotes<T>) -> Self {
240 Self(Cow::Owned(
241 IntoIterator::into_iter([Component::from(refs), notes.into()])
242 .collect::<RefString>()
243 .and(name),
244 ))
245 }
246}
247
248impl<T> From<lit::RefsRemotes<T>> for Qualified<'_>
249where
250 T: AsRef<RefStr>,
251{
252 #[inline]
253 fn from((refs, remotes, name): lit::RefsRemotes<T>) -> Self {
254 Self(Cow::Owned(
255 IntoIterator::into_iter([Component::from(refs), remotes.into()])
256 .collect::<RefString>()
257 .and(name),
258 ))
259 }
260}
261
262impl Display for Qualified<'_> {
263 #[inline]
264 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
265 self.0.fmt(f)
266 }
267}
268
269#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]
294pub struct Namespaced<'a>(Cow<'a, RefStr>);
295
296impl<'a> Namespaced<'a> {
297 pub fn namespace(&'a self) -> Component<'a> {
298 self.components().nth(2).unwrap()
299 }
300
301 pub fn strip_namespace<'b>(&self) -> Qualified<'b> {
302 const REFS_NAMESPACES: &RefStr = RefStr::from_str("refs/namespaces");
303
304 Qualified(Cow::Owned(
305 self.strip_prefix(REFS_NAMESPACES)
306 .unwrap()
307 .components()
308 .skip(1)
309 .collect(),
310 ))
311 }
312
313 pub fn strip_namespace_recursive<'b>(&self) -> Qualified<'b> {
314 let mut strip = self.strip_namespace();
315 while let Some(ns) = strip.to_namespaced() {
316 strip = ns.strip_namespace();
317 }
318 strip
319 }
320
321 #[inline]
322 pub fn to_owned<'b>(&self) -> Namespaced<'b> {
323 Namespaced(Cow::Owned(self.0.clone().into_owned()))
324 }
325
326 #[inline]
327 pub fn into_owned<'b>(self) -> Namespaced<'b> {
328 Namespaced(Cow::Owned(self.0.into_owned()))
329 }
330
331 #[inline]
332 pub fn into_qualified(self) -> Qualified<'a> {
333 self.into()
334 }
335}
336
337impl Deref for Namespaced<'_> {
338 type Target = RefStr;
339
340 #[inline]
341 fn deref(&self) -> &Self::Target {
342 &self.0
343 }
344}
345
346impl AsRef<RefStr> for Namespaced<'_> {
347 #[inline]
348 fn as_ref(&self) -> &RefStr {
349 self
350 }
351}
352
353impl AsRef<str> for Namespaced<'_> {
354 #[inline]
355 fn as_ref(&self) -> &str {
356 self.0.as_str()
357 }
358}
359
360impl<'a> From<Namespaced<'a>> for Qualified<'a> {
361 #[inline]
362 fn from(ns: Namespaced<'a>) -> Self {
363 Self(ns.0)
364 }
365}
366
367impl<'a> From<&'a RefStr> for Option<Namespaced<'a>> {
368 fn from(rs: &'a RefStr) -> Self {
369 let mut cs = rs.iter();
370 match (cs.next()?, cs.next()?, cs.next()?, cs.next()?) {
371 ("refs", "namespaces", _, "refs" | "HEAD") => Some(Namespaced(Cow::from(rs))),
372
373 _ => None,
374 }
375 }
376}
377
378impl<'a, T> From<lit::RefsNamespaces<'_, T>> for Namespaced<'static>
379where
380 T: Into<Component<'a>>,
381{
382 #[inline]
383 fn from((refs, namespaces, namespace, name): lit::RefsNamespaces<T>) -> Self {
384 Self(Cow::Owned(
385 IntoIterator::into_iter([refs.into(), namespaces.into(), namespace.into()])
386 .collect::<RefString>()
387 .and(name),
388 ))
389 }
390}
391
392impl Display for Namespaced<'_> {
393 #[inline]
394 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
395 self.0.fmt(f)
396 }
397}