1use std::marker::PhantomData;
2
3use git_ext::ref_format::{
4 self, refname,
5 refspec::{self, PatternString, QualifiedPattern},
6 Qualified, RefStr, RefString,
7};
8use thiserror::Error;
9
10use crate::{Branch, Local, Namespace, Remote, Tag};
11
12#[derive(Debug, Error)]
13pub enum Error {
14 #[error(transparent)]
15 RefFormat(#[from] ref_format::Error),
16}
17
18#[derive(Clone, Debug)]
20pub struct Glob<T> {
21 globs: Vec<QualifiedPattern<'static>>,
22 glob_type: PhantomData<T>, }
24
25impl<T> Default for Glob<T> {
26 fn default() -> Self {
27 Self {
28 globs: Default::default(),
29 glob_type: PhantomData,
30 }
31 }
32}
33
34impl<T> Glob<T> {
35 pub fn globs(&self) -> impl Iterator<Item = &QualifiedPattern<'static>> {
37 self.globs.iter()
38 }
39
40 pub fn and(mut self, other: impl Into<Self>) -> Self {
46 self.globs.extend(other.into().globs);
47 self
48 }
49}
50
51impl Glob<Namespace> {
52 pub fn all_namespaces() -> Self {
54 Self::namespaces(refspec::pattern!("*"))
55 }
56
57 pub fn namespaces(glob: PatternString) -> Self {
59 let globs = vec![Self::qualify(glob)];
60 Self {
61 globs,
62 glob_type: PhantomData,
63 }
64 }
65
66 pub fn insert(mut self, glob: PatternString) -> Self {
68 self.globs.push(Self::qualify(glob));
69 self
70 }
71
72 fn qualify(glob: PatternString) -> QualifiedPattern<'static> {
73 qualify(&refname!("refs/namespaces"), glob).expect("BUG: pattern is qualified")
74 }
75}
76
77impl FromIterator<PatternString> for Glob<Namespace> {
78 fn from_iter<T: IntoIterator<Item = PatternString>>(iter: T) -> Self {
79 let globs = iter
80 .into_iter()
81 .map(|pat| {
82 qualify(&refname!("refs/namespaces"), pat).expect("BUG: pattern is qualified")
83 })
84 .collect();
85
86 Self {
87 globs,
88 glob_type: PhantomData,
89 }
90 }
91}
92
93impl Extend<PatternString> for Glob<Namespace> {
94 fn extend<T: IntoIterator<Item = PatternString>>(&mut self, iter: T) {
95 self.globs.extend(iter.into_iter().map(|pat| {
96 qualify(&refname!("refs/namespaces"), pat).expect("BUG: pattern is qualified")
97 }))
98 }
99}
100
101impl Glob<Tag> {
102 pub fn all_tags() -> Self {
104 Self::tags(refspec::pattern!("*"))
105 }
106
107 pub fn tags(glob: PatternString) -> Self {
109 let globs = vec![Self::qualify(glob)];
110 Self {
111 globs,
112 glob_type: PhantomData,
113 }
114 }
115
116 pub fn insert(mut self, glob: PatternString) -> Self {
118 self.globs.push(Self::qualify(glob));
119 self
120 }
121
122 fn qualify(glob: PatternString) -> QualifiedPattern<'static> {
123 qualify(&refname!("refs/tags"), glob).expect("BUG: pattern is qualified")
124 }
125}
126
127impl FromIterator<PatternString> for Glob<Tag> {
128 fn from_iter<T: IntoIterator<Item = PatternString>>(iter: T) -> Self {
129 let globs = iter
130 .into_iter()
131 .map(|pat| qualify(&refname!("refs/tags"), pat).expect("BUG: pattern is qualified"))
132 .collect();
133
134 Self {
135 globs,
136 glob_type: PhantomData,
137 }
138 }
139}
140
141impl Extend<PatternString> for Glob<Tag> {
142 fn extend<T: IntoIterator<Item = PatternString>>(&mut self, iter: T) {
143 self.globs.extend(
144 iter.into_iter()
145 .map(|pat| qualify(&refname!("refs/tag"), pat).expect("BUG: pattern is qualified")),
146 )
147 }
148}
149
150impl Glob<Local> {
151 pub fn all_heads() -> Self {
153 Self::heads(refspec::pattern!("*"))
154 }
155
156 pub fn heads(glob: PatternString) -> Self {
158 let globs = vec![Self::qualify_heads(glob)];
159 Self {
160 globs,
161 glob_type: PhantomData,
162 }
163 }
164
165 pub fn insert(mut self, glob: PatternString) -> Self {
167 self.globs.push(Self::qualify_heads(glob));
168 self
169 }
170
171 pub fn branches(self) -> Glob<Branch> {
183 self.into()
184 }
185
186 fn qualify_heads(glob: PatternString) -> QualifiedPattern<'static> {
187 qualify(&refname!("refs/heads"), glob).expect("BUG: pattern is qualified")
188 }
189}
190
191impl FromIterator<PatternString> for Glob<Local> {
192 fn from_iter<T: IntoIterator<Item = PatternString>>(iter: T) -> Self {
193 let globs = iter
194 .into_iter()
195 .map(|pat| qualify(&refname!("refs/heads"), pat).expect("BUG: pattern is qualified"))
196 .collect();
197
198 Self {
199 globs,
200 glob_type: PhantomData,
201 }
202 }
203}
204
205impl Extend<PatternString> for Glob<Local> {
206 fn extend<T: IntoIterator<Item = PatternString>>(&mut self, iter: T) {
207 self.globs.extend(
208 iter.into_iter().map(|pat| {
209 qualify(&refname!("refs/heads"), pat).expect("BUG: pattern is qualified")
210 }),
211 )
212 }
213}
214
215impl From<Glob<Local>> for Glob<Branch> {
216 fn from(Glob { globs, .. }: Glob<Local>) -> Self {
217 Self {
218 globs,
219 glob_type: PhantomData,
220 }
221 }
222}
223
224impl Glob<Remote> {
225 pub fn all_remotes() -> Self {
227 Self::remotes(refspec::pattern!("*"))
228 }
229
230 pub fn remotes(glob: PatternString) -> Self {
232 let globs = vec![Self::qualify_remotes(glob)];
233 Self {
234 globs,
235 glob_type: PhantomData,
236 }
237 }
238
239 pub fn insert(mut self, glob: PatternString) -> Self {
241 self.globs.push(Self::qualify_remotes(glob));
242 self
243 }
244
245 pub fn branches(self) -> Glob<Branch> {
257 self.into()
258 }
259
260 fn qualify_remotes(glob: PatternString) -> QualifiedPattern<'static> {
261 qualify(&refname!("refs/remotes"), glob).expect("BUG: pattern is qualified")
262 }
263}
264
265impl FromIterator<PatternString> for Glob<Remote> {
266 fn from_iter<T: IntoIterator<Item = PatternString>>(iter: T) -> Self {
267 let globs = iter
268 .into_iter()
269 .map(|pat| qualify(&refname!("refs/remotes"), pat).expect("BUG: pattern is qualified"))
270 .collect();
271
272 Self {
273 globs,
274 glob_type: PhantomData,
275 }
276 }
277}
278
279impl Extend<PatternString> for Glob<Remote> {
280 fn extend<T: IntoIterator<Item = PatternString>>(&mut self, iter: T) {
281 self.globs.extend(
282 iter.into_iter().map(|pat| {
283 qualify(&refname!("refs/remotes"), pat).expect("BUG: pattern is qualified")
284 }),
285 )
286 }
287}
288
289impl From<Glob<Remote>> for Glob<Branch> {
290 fn from(Glob { globs, .. }: Glob<Remote>) -> Self {
291 Self {
292 globs,
293 glob_type: PhantomData,
294 }
295 }
296}
297
298impl Glob<Qualified<'_>> {
299 pub fn all_category<R: AsRef<RefStr>>(category: R) -> Self {
300 Self {
301 globs: vec![Self::qualify_category(category, refspec::pattern!("*"))],
302 glob_type: PhantomData,
303 }
304 }
305
306 pub fn categories<R>(category: R, glob: PatternString) -> Self
308 where
309 R: AsRef<RefStr>,
310 {
311 let globs = vec![Self::qualify_category(category, glob)];
312 Self {
313 globs,
314 glob_type: PhantomData,
315 }
316 }
317
318 pub fn insert<R>(mut self, category: R, glob: PatternString) -> Self
320 where
321 R: AsRef<RefStr>,
322 {
323 self.globs.push(Self::qualify_category(category, glob));
324 self
325 }
326
327 fn qualify_category<R>(category: R, glob: PatternString) -> QualifiedPattern<'static>
328 where
329 R: AsRef<RefStr>,
330 {
331 let prefix = refname!("refs").and(category);
332 qualify(&prefix, glob).expect("BUG: pattern is qualified")
333 }
334}
335
336fn qualify(prefix: &RefString, glob: PatternString) -> Option<QualifiedPattern<'static>> {
337 prefix.to_pattern(glob).qualified().map(|q| q.into_owned())
338}