1use std::marker::PhantomData;
19
20use git_ext::ref_format::{
21 self, refname,
22 refspec::{self, PatternString, QualifiedPattern},
23 Qualified, RefStr, RefString,
24};
25use thiserror::Error;
26
27use crate::{Branch, Local, Namespace, Remote, Tag};
28
29#[derive(Debug, Error)]
30pub enum Error {
31 #[error(transparent)]
32 RefFormat(#[from] ref_format::Error),
33}
34
35#[derive(Clone, Debug)]
37pub struct Glob<T> {
38 globs: Vec<QualifiedPattern<'static>>,
39 glob_type: PhantomData<T>, }
41
42impl<T> Default for Glob<T> {
43 fn default() -> Self {
44 Self {
45 globs: Default::default(),
46 glob_type: PhantomData,
47 }
48 }
49}
50
51impl<T> Glob<T> {
52 pub fn globs(&self) -> impl Iterator<Item = &QualifiedPattern<'static>> {
54 self.globs.iter()
55 }
56
57 pub fn and(mut self, other: impl Into<Self>) -> Self {
63 self.globs.extend(other.into().globs);
64 self
65 }
66}
67
68impl Glob<Namespace> {
69 pub fn all_namespaces() -> Self {
71 Self::namespaces(refspec::pattern!("*"))
72 }
73
74 pub fn namespaces(glob: PatternString) -> Self {
76 let globs = vec![Self::qualify(glob)];
77 Self {
78 globs,
79 glob_type: PhantomData,
80 }
81 }
82
83 pub fn insert(mut self, glob: PatternString) -> Self {
85 self.globs.push(Self::qualify(glob));
86 self
87 }
88
89 fn qualify(glob: PatternString) -> QualifiedPattern<'static> {
90 qualify(&refname!("refs/namespaces"), glob).expect("BUG: pattern is qualified")
91 }
92}
93
94impl FromIterator<PatternString> for Glob<Namespace> {
95 fn from_iter<T: IntoIterator<Item = PatternString>>(iter: T) -> Self {
96 let globs = iter
97 .into_iter()
98 .map(|pat| {
99 qualify(&refname!("refs/namespaces"), pat).expect("BUG: pattern is qualified")
100 })
101 .collect();
102
103 Self {
104 globs,
105 glob_type: PhantomData,
106 }
107 }
108}
109
110impl Extend<PatternString> for Glob<Namespace> {
111 fn extend<T: IntoIterator<Item = PatternString>>(&mut self, iter: T) {
112 self.globs.extend(iter.into_iter().map(|pat| {
113 qualify(&refname!("refs/namespaces"), pat).expect("BUG: pattern is qualified")
114 }))
115 }
116}
117
118impl Glob<Tag> {
119 pub fn all_tags() -> Self {
121 Self::tags(refspec::pattern!("*"))
122 }
123
124 pub fn tags(glob: PatternString) -> Self {
126 let globs = vec![Self::qualify(glob)];
127 Self {
128 globs,
129 glob_type: PhantomData,
130 }
131 }
132
133 pub fn insert(mut self, glob: PatternString) -> Self {
135 self.globs.push(Self::qualify(glob));
136 self
137 }
138
139 fn qualify(glob: PatternString) -> QualifiedPattern<'static> {
140 qualify(&refname!("refs/tags"), glob).expect("BUG: pattern is qualified")
141 }
142}
143
144impl FromIterator<PatternString> for Glob<Tag> {
145 fn from_iter<T: IntoIterator<Item = PatternString>>(iter: T) -> Self {
146 let globs = iter
147 .into_iter()
148 .map(|pat| qualify(&refname!("refs/tags"), pat).expect("BUG: pattern is qualified"))
149 .collect();
150
151 Self {
152 globs,
153 glob_type: PhantomData,
154 }
155 }
156}
157
158impl Extend<PatternString> for Glob<Tag> {
159 fn extend<T: IntoIterator<Item = PatternString>>(&mut self, iter: T) {
160 self.globs.extend(
161 iter.into_iter()
162 .map(|pat| qualify(&refname!("refs/tag"), pat).expect("BUG: pattern is qualified")),
163 )
164 }
165}
166
167impl Glob<Local> {
168 pub fn all_heads() -> Self {
170 Self::heads(refspec::pattern!("*"))
171 }
172
173 pub fn heads(glob: PatternString) -> Self {
175 let globs = vec![Self::qualify_heads(glob)];
176 Self {
177 globs,
178 glob_type: PhantomData,
179 }
180 }
181
182 pub fn insert(mut self, glob: PatternString) -> Self {
184 self.globs.push(Self::qualify_heads(glob));
185 self
186 }
187
188 pub fn branches(self) -> Glob<Branch> {
200 self.into()
201 }
202
203 fn qualify_heads(glob: PatternString) -> QualifiedPattern<'static> {
204 qualify(&refname!("refs/heads"), glob).expect("BUG: pattern is qualified")
205 }
206}
207
208impl FromIterator<PatternString> for Glob<Local> {
209 fn from_iter<T: IntoIterator<Item = PatternString>>(iter: T) -> Self {
210 let globs = iter
211 .into_iter()
212 .map(|pat| qualify(&refname!("refs/heads"), pat).expect("BUG: pattern is qualified"))
213 .collect();
214
215 Self {
216 globs,
217 glob_type: PhantomData,
218 }
219 }
220}
221
222impl Extend<PatternString> for Glob<Local> {
223 fn extend<T: IntoIterator<Item = PatternString>>(&mut self, iter: T) {
224 self.globs.extend(
225 iter.into_iter().map(|pat| {
226 qualify(&refname!("refs/heads"), pat).expect("BUG: pattern is qualified")
227 }),
228 )
229 }
230}
231
232impl From<Glob<Local>> for Glob<Branch> {
233 fn from(Glob { globs, .. }: Glob<Local>) -> Self {
234 Self {
235 globs,
236 glob_type: PhantomData,
237 }
238 }
239}
240
241impl Glob<Remote> {
242 pub fn all_remotes() -> Self {
244 Self::remotes(refspec::pattern!("*"))
245 }
246
247 pub fn remotes(glob: PatternString) -> Self {
249 let globs = vec![Self::qualify_remotes(glob)];
250 Self {
251 globs,
252 glob_type: PhantomData,
253 }
254 }
255
256 pub fn insert(mut self, glob: PatternString) -> Self {
258 self.globs.push(Self::qualify_remotes(glob));
259 self
260 }
261
262 pub fn branches(self) -> Glob<Branch> {
274 self.into()
275 }
276
277 fn qualify_remotes(glob: PatternString) -> QualifiedPattern<'static> {
278 qualify(&refname!("refs/remotes"), glob).expect("BUG: pattern is qualified")
279 }
280}
281
282impl FromIterator<PatternString> for Glob<Remote> {
283 fn from_iter<T: IntoIterator<Item = PatternString>>(iter: T) -> Self {
284 let globs = iter
285 .into_iter()
286 .map(|pat| qualify(&refname!("refs/remotes"), pat).expect("BUG: pattern is qualified"))
287 .collect();
288
289 Self {
290 globs,
291 glob_type: PhantomData,
292 }
293 }
294}
295
296impl Extend<PatternString> for Glob<Remote> {
297 fn extend<T: IntoIterator<Item = PatternString>>(&mut self, iter: T) {
298 self.globs.extend(
299 iter.into_iter().map(|pat| {
300 qualify(&refname!("refs/remotes"), pat).expect("BUG: pattern is qualified")
301 }),
302 )
303 }
304}
305
306impl From<Glob<Remote>> for Glob<Branch> {
307 fn from(Glob { globs, .. }: Glob<Remote>) -> Self {
308 Self {
309 globs,
310 glob_type: PhantomData,
311 }
312 }
313}
314
315impl Glob<Qualified<'_>> {
316 pub fn all_category<R: AsRef<RefStr>>(category: R) -> Self {
317 Self {
318 globs: vec![Self::qualify_category(category, refspec::pattern!("*"))],
319 glob_type: PhantomData,
320 }
321 }
322
323 pub fn categories<R>(category: R, glob: PatternString) -> Self
325 where
326 R: AsRef<RefStr>,
327 {
328 let globs = vec![Self::qualify_category(category, glob)];
329 Self {
330 globs,
331 glob_type: PhantomData,
332 }
333 }
334
335 pub fn insert<R>(mut self, category: R, glob: PatternString) -> Self
337 where
338 R: AsRef<RefStr>,
339 {
340 self.globs.push(Self::qualify_category(category, glob));
341 self
342 }
343
344 fn qualify_category<R>(category: R, glob: PatternString) -> QualifiedPattern<'static>
345 where
346 R: AsRef<RefStr>,
347 {
348 let prefix = refname!("refs").and(category);
349 qualify(&prefix, glob).expect("BUG: pattern is qualified")
350 }
351}
352
353fn qualify(prefix: &RefString, glob: PatternString) -> Option<QualifiedPattern<'static>> {
354 prefix.to_pattern(glob).qualified().map(|q| q.into_owned())
355}