1use std::{
7 borrow::{Borrow, Cow},
8 convert::TryFrom,
9 fmt::{self, Display, Write as _},
10 iter::FromIterator,
11 ops::Deref,
12};
13
14use thiserror::Error;
15
16use crate::{check, lit, Namespaced, Qualified, RefStr, RefString};
17
18mod iter;
19pub use iter::{component, Component, Components, Iter};
20
21pub const STAR: &PatternStr = PatternStr::from_str("*");
22
23const CHECK_OPTS: check::Options = check::Options {
24 allow_onelevel: true,
25 allow_pattern: true,
26};
27
28#[repr(transparent)]
29#[derive(Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]
30pub struct PatternStr(str);
31
32impl PatternStr {
33 #[inline]
34 pub fn try_from_str(s: &str) -> Result<&Self, check::Error> {
35 TryFrom::try_from(s)
36 }
37
38 #[inline]
39 pub fn as_str(&self) -> &str {
40 self
41 }
42
43 pub fn join<R>(&self, other: R) -> PatternString
44 where
45 R: AsRef<RefStr>,
46 {
47 self._join(other.as_ref())
48 }
49
50 fn _join(&self, other: &RefStr) -> PatternString {
51 let mut buf = self.to_owned();
52 buf.push(other);
53 buf
54 }
55
56 #[inline]
57 pub fn qualified(&self) -> Option<QualifiedPattern> {
58 QualifiedPattern::from_patternstr(self)
59 }
60
61 #[inline]
62 pub fn to_namespaced(&self) -> Option<NamespacedPattern> {
63 self.into()
64 }
65
66 #[inline]
67 pub fn iter(&self) -> Iter {
68 self.0.split('/')
69 }
70
71 #[inline]
72 pub fn components(&self) -> Components {
73 Components::from(self)
74 }
75
76 pub(crate) const fn from_str(s: &str) -> &PatternStr {
77 unsafe { &*(s as *const str as *const PatternStr) }
78 }
79}
80
81impl Deref for PatternStr {
82 type Target = str;
83
84 #[inline]
85 fn deref(&self) -> &Self::Target {
86 &self.0
87 }
88}
89
90impl AsRef<str> for PatternStr {
91 #[inline]
92 fn as_ref(&self) -> &str {
93 self
94 }
95}
96
97impl AsRef<Self> for PatternStr {
98 #[inline]
99 fn as_ref(&self) -> &Self {
100 self
101 }
102}
103
104impl<'a> TryFrom<&'a str> for &'a PatternStr {
105 type Error = check::Error;
106
107 #[inline]
108 fn try_from(s: &'a str) -> Result<Self, Self::Error> {
109 check::ref_format(CHECK_OPTS, s).map(|()| PatternStr::from_str(s))
110 }
111}
112
113impl<'a> From<&'a RefStr> for &'a PatternStr {
114 #[inline]
115 fn from(rs: &'a RefStr) -> Self {
116 PatternStr::from_str(rs.as_str())
117 }
118}
119
120impl<'a> From<&'a PatternStr> for Cow<'a, PatternStr> {
121 #[inline]
122 fn from(p: &'a PatternStr) -> Cow<'a, PatternStr> {
123 Cow::Borrowed(p)
124 }
125}
126
127impl Display for PatternStr {
128 #[inline]
129 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
130 f.write_str(self)
131 }
132}
133
134#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]
135pub struct PatternString(pub(crate) String);
136
137impl PatternString {
138 #[inline]
139 pub fn as_str(&self) -> &str {
140 self.as_ref()
141 }
142
143 #[inline]
144 pub fn as_pattern_str(&self) -> &PatternStr {
145 self.as_ref()
146 }
147
148 #[inline]
149 pub fn from_components<'a, T>(iter: T) -> Result<Self, DuplicateGlob>
150 where
151 T: IntoIterator<Item = Component<'a>>,
152 {
153 iter.into_iter().collect()
154 }
155
156 #[inline]
157 pub fn and<R>(mut self, other: R) -> Self
158 where
159 R: AsRef<RefStr>,
160 {
161 self._push(other.as_ref());
162 self
163 }
164
165 #[inline]
166 pub fn push<R>(&mut self, other: R)
167 where
168 R: AsRef<RefStr>,
169 {
170 self._push(other.as_ref())
171 }
172
173 fn _push(&mut self, other: &RefStr) {
174 self.0.push('/');
175 self.0.push_str(other.as_str());
176 }
177
178 #[inline]
179 pub fn pop(&mut self) -> bool {
180 match self.0.rfind('/') {
181 None => false,
182 Some(idx) => {
183 self.0.truncate(idx);
184 true
185 }
186 }
187 }
188}
189
190impl Deref for PatternString {
191 type Target = PatternStr;
192
193 #[inline]
194 fn deref(&self) -> &Self::Target {
195 self.borrow()
196 }
197}
198
199impl AsRef<PatternStr> for PatternString {
200 #[inline]
201 fn as_ref(&self) -> &PatternStr {
202 self
203 }
204}
205
206impl AsRef<str> for PatternString {
207 #[inline]
208 fn as_ref(&self) -> &str {
209 self.0.as_str()
210 }
211}
212
213impl Borrow<PatternStr> for PatternString {
214 #[inline]
215 fn borrow(&self) -> &PatternStr {
216 PatternStr::from_str(self.0.as_str())
217 }
218}
219
220impl ToOwned for PatternStr {
221 type Owned = PatternString;
222
223 #[inline]
224 fn to_owned(&self) -> Self::Owned {
225 PatternString(self.0.to_owned())
226 }
227}
228
229impl From<RefString> for PatternString {
230 #[inline]
231 fn from(rs: RefString) -> Self {
232 Self(rs.into())
233 }
234}
235
236impl<'a> From<&'a PatternString> for Cow<'a, PatternStr> {
237 #[inline]
238 fn from(p: &'a PatternString) -> Cow<'a, PatternStr> {
239 Cow::Borrowed(p.as_ref())
240 }
241}
242
243impl From<PatternString> for String {
244 #[inline]
245 fn from(p: PatternString) -> Self {
246 p.0
247 }
248}
249
250impl TryFrom<&str> for PatternString {
251 type Error = check::Error;
252
253 #[inline]
254 fn try_from(s: &str) -> Result<Self, Self::Error> {
255 PatternStr::try_from_str(s).map(ToOwned::to_owned)
256 }
257}
258
259impl TryFrom<String> for PatternString {
260 type Error = check::Error;
261
262 #[inline]
263 fn try_from(s: String) -> Result<Self, Self::Error> {
264 check::ref_format(CHECK_OPTS, s.as_str()).map(|()| PatternString(s))
265 }
266}
267
268#[derive(Debug, Error)]
269#[error("more than one '*' encountered")]
270pub struct DuplicateGlob;
271
272impl<'a> FromIterator<Component<'a>> for Result<PatternString, DuplicateGlob> {
273 fn from_iter<T>(iter: T) -> Self
274 where
275 T: IntoIterator<Item = Component<'a>>,
276 {
277 use Component::*;
278
279 let mut buf = String::new();
280 let mut seen_glob = false;
281 for c in iter {
282 if let Glob(_) = c {
283 if seen_glob {
284 return Err(DuplicateGlob);
285 }
286
287 seen_glob = true;
288 }
289
290 buf.push_str(c.as_str());
291 buf.push('/');
292 }
293 buf.truncate(buf.len() - 1);
294
295 Ok(PatternString(buf))
296 }
297}
298
299impl Display for PatternString {
300 #[inline]
301 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
302 f.write_str(&self.0)
303 }
304}
305
306#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]
312pub struct QualifiedPattern<'a>(pub(crate) Cow<'a, PatternStr>);
313
314impl<'a> QualifiedPattern<'a> {
315 pub fn from_patternstr(r: impl Into<Cow<'a, PatternStr>>) -> Option<Self> {
316 Self::_from_patternstr(r.into())
317 }
318
319 fn _from_patternstr(r: Cow<'a, PatternStr>) -> Option<Self> {
320 let mut iter = r.iter();
321 match (iter.next()?, iter.next()?, iter.next()?) {
322 ("refs", _, _) => Some(QualifiedPattern(r)),
323 _ => None,
324 }
325 }
326
327 #[inline]
328 pub fn as_str(&self) -> &str {
329 self.as_ref()
330 }
331
332 #[inline]
333 pub fn join<'b, R>(&self, other: R) -> QualifiedPattern<'b>
334 where
335 R: AsRef<RefStr>,
336 {
337 QualifiedPattern(Cow::Owned(self.0.join(other)))
338 }
339
340 #[inline]
341 pub fn to_namespaced(&self) -> Option<NamespacedPattern> {
342 self.0.as_ref().into()
343 }
344
345 pub fn with_namespace<'b>(
350 &self,
351 ns: Component<'b>,
352 ) -> Result<NamespacedPattern<'a>, DuplicateGlob> {
353 PatternString::from_components(
354 IntoIterator::into_iter([lit::Refs.into(), lit::Namespaces.into(), ns])
355 .chain(self.components()),
356 )
357 .map(|pat| NamespacedPattern(Cow::Owned(pat)))
358 }
359
360 pub fn non_empty_iter(&self) -> (&str, &str, &str, Iter) {
362 let mut iter = self.iter();
363 (
364 iter.next().unwrap(),
365 iter.next().unwrap(),
366 iter.next().unwrap(),
367 iter,
368 )
369 }
370
371 pub fn non_empty_components(&self) -> (Component, Component, Component, Components) {
378 let mut cs = self.components();
379 (
380 cs.next().unwrap(),
381 cs.next().unwrap(),
382 cs.next().unwrap(),
383 cs,
384 )
385 }
386
387 #[inline]
388 pub fn to_owned<'b>(&self) -> QualifiedPattern<'b> {
389 QualifiedPattern(Cow::Owned(self.0.clone().into_owned()))
390 }
391
392 #[inline]
393 pub fn into_owned<'b>(self) -> QualifiedPattern<'b> {
394 QualifiedPattern(Cow::Owned(self.0.into_owned()))
395 }
396
397 #[inline]
398 pub fn into_patternstring(self) -> PatternString {
399 self.into()
400 }
401}
402
403impl Deref for QualifiedPattern<'_> {
404 type Target = PatternStr;
405
406 #[inline]
407 fn deref(&self) -> &Self::Target {
408 &self.0
409 }
410}
411
412impl AsRef<PatternStr> for QualifiedPattern<'_> {
413 #[inline]
414 fn as_ref(&self) -> &PatternStr {
415 self
416 }
417}
418
419impl AsRef<str> for QualifiedPattern<'_> {
420 #[inline]
421 fn as_ref(&self) -> &str {
422 self.0.as_str()
423 }
424}
425
426impl AsRef<Self> for QualifiedPattern<'_> {
427 #[inline]
428 fn as_ref(&self) -> &Self {
429 self
430 }
431}
432
433impl Display for QualifiedPattern<'_> {
434 #[inline]
435 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
436 self.0.fmt(f)
437 }
438}
439
440impl<'a> From<Qualified<'a>> for QualifiedPattern<'a> {
441 #[inline]
442 fn from(q: Qualified<'a>) -> Self {
443 Self(Cow::Owned(q.into_refstring().into()))
444 }
445}
446
447impl<'a> From<QualifiedPattern<'a>> for Cow<'a, PatternStr> {
448 #[inline]
449 fn from(q: QualifiedPattern<'a>) -> Self {
450 q.0
451 }
452}
453
454impl From<QualifiedPattern<'_>> for PatternString {
455 #[inline]
456 fn from(q: QualifiedPattern) -> Self {
457 q.0.into_owned()
458 }
459}
460
461#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]
485pub struct NamespacedPattern<'a>(Cow<'a, PatternStr>);
486
487impl<'a> NamespacedPattern<'a> {
488 pub fn namespace(&self) -> Component {
489 self.components().nth(2).unwrap()
490 }
491
492 pub fn strip_namespace(&self) -> PatternString {
493 PatternString::from_components(self.components().skip(3))
494 .expect("BUG: NamespacedPattern was constructed with a duplicate glob")
495 }
496
497 pub fn strip_namespace_recursive(&self) -> PatternString {
498 let mut strip = self.strip_namespace();
499 while let Some(ns) = strip.to_namespaced() {
500 strip = ns.strip_namespace();
501 }
502 strip
503 }
504
505 #[inline]
506 pub fn to_owned<'b>(&self) -> NamespacedPattern<'b> {
507 NamespacedPattern(Cow::Owned(self.0.clone().into_owned()))
508 }
509
510 #[inline]
511 pub fn into_owned<'b>(self) -> NamespacedPattern<'b> {
512 NamespacedPattern(Cow::Owned(self.0.into_owned()))
513 }
514
515 #[inline]
516 pub fn into_qualified(self) -> QualifiedPattern<'a> {
517 self.into()
518 }
519}
520
521impl Deref for NamespacedPattern<'_> {
522 type Target = PatternStr;
523
524 #[inline]
525 fn deref(&self) -> &Self::Target {
526 self.borrow()
527 }
528}
529
530impl AsRef<PatternStr> for NamespacedPattern<'_> {
531 #[inline]
532 fn as_ref(&self) -> &PatternStr {
533 self.0.as_ref()
534 }
535}
536
537impl AsRef<str> for NamespacedPattern<'_> {
538 #[inline]
539 fn as_ref(&self) -> &str {
540 self.0.as_str()
541 }
542}
543
544impl Borrow<PatternStr> for NamespacedPattern<'_> {
545 #[inline]
546 fn borrow(&self) -> &PatternStr {
547 PatternStr::from_str(self.0.as_str())
548 }
549}
550
551impl<'a> From<Namespaced<'a>> for NamespacedPattern<'a> {
552 #[inline]
553 fn from(ns: Namespaced<'a>) -> Self {
554 NamespacedPattern(Cow::Owned(ns.to_ref_string().into()))
555 }
556}
557
558impl<'a> From<NamespacedPattern<'a>> for QualifiedPattern<'a> {
559 #[inline]
560 fn from(ns: NamespacedPattern<'a>) -> Self {
561 Self(ns.0)
562 }
563}
564
565impl<'a> From<&'a PatternStr> for Option<NamespacedPattern<'a>> {
566 fn from(rs: &'a PatternStr) -> Self {
567 let mut cs = rs.iter();
568 match (cs.next()?, cs.next()?, cs.next()?, cs.next()?) {
569 ("refs", "namespaces", _, "refs") => Some(NamespacedPattern(Cow::from(rs))),
570
571 _ => None,
572 }
573 }
574}
575
576impl Display for NamespacedPattern<'_> {
577 #[inline]
578 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
579 self.0.fmt(f)
580 }
581}
582
583#[derive(Clone, Debug, PartialEq, Eq)]
591pub struct Refspec<T, U> {
592 pub src: T,
593 pub dst: U,
594 pub force: bool,
595}
596
597impl<T, U> fmt::Display for Refspec<T, U>
598where
599 T: fmt::Display,
600 U: fmt::Display,
601{
602 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
603 if self.force {
604 f.write_char('+')?;
605 }
606 write!(f, "{}:{}", self.src, self.dst)
607 }
608}