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