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