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