git_ref_format_core/
name.rs1use std::{
2 borrow::{Borrow, Cow},
3 convert::TryFrom,
4 fmt::{self, Display},
5 iter::{Extend, FromIterator},
6 ops::Deref,
7};
8
9use crate::{
10 check,
11 refspec::{PatternStr, PatternString},
12 Namespaced, Qualified,
13};
14
15mod iter;
16pub use iter::{component, Component, Components, Iter};
17
18#[cfg(feature = "percent-encoding")]
19pub use percent_encoding::PercentEncode;
20
21pub const HEADS: &RefStr = RefStr::from_str(str::HEADS);
22pub const MAIN: &RefStr = RefStr::from_str(str::MAIN);
23pub const MASTER: &RefStr = RefStr::from_str(str::MASTER);
24pub const NAMESPACES: &RefStr = RefStr::from_str(str::NAMESPACES);
25pub const NOTES: &RefStr = RefStr::from_str(str::NOTES);
26pub const ORIGIN: &RefStr = RefStr::from_str(str::ORIGIN);
27pub const REFS: &RefStr = RefStr::from_str(str::REFS);
28pub const REMOTES: &RefStr = RefStr::from_str(str::REMOTES);
29pub const TAGS: &RefStr = RefStr::from_str(str::TAGS);
30
31pub const REFS_HEADS_MAIN: &RefStr = RefStr::from_str(str::REFS_HEADS_MAIN);
32pub const REFS_HEADS_MASTER: &RefStr = RefStr::from_str(str::REFS_HEADS_MASTER);
33
34pub mod str {
35 pub const HEADS: &str = "heads";
36 pub const MAIN: &str = "main";
37 pub const MASTER: &str = "master";
38 pub const NAMESPACES: &str = "namespaces";
39 pub const NOTES: &str = "notes";
40 pub const ORIGIN: &str = "origin";
41 pub const REFS: &str = "refs";
42 pub const REMOTES: &str = "remotes";
43 pub const TAGS: &str = "tags";
44
45 pub const REFS_HEADS_MAIN: &str = "refs/heads/main";
46 pub const REFS_HEADS_MASTER: &str = "refs/heads/master";
47}
48
49pub mod bytes {
50 use super::str;
51
52 pub const HEADS: &[u8] = str::HEADS.as_bytes();
53 pub const MAIN: &[u8] = str::MAIN.as_bytes();
54 pub const MASTER: &[u8] = str::MASTER.as_bytes();
55 pub const NAMESPACES: &[u8] = str::NAMESPACES.as_bytes();
56 pub const NOTES: &[u8] = str::NOTES.as_bytes();
57 pub const ORIGIN: &[u8] = str::ORIGIN.as_bytes();
58 pub const REFS: &[u8] = str::REFS.as_bytes();
59 pub const REMOTES: &[u8] = str::REMOTES.as_bytes();
60 pub const TAGS: &[u8] = str::TAGS.as_bytes();
61
62 pub const REFS_HEADS_MAIN: &[u8] = str::REFS_HEADS_MAIN.as_bytes();
63 pub const REFS_HEADS_MASTER: &[u8] = str::REFS_HEADS_MASTER.as_bytes();
64}
65
66const CHECK_OPTS: check::Options = check::Options {
67 allow_pattern: false,
68 allow_onelevel: true,
69};
70
71#[repr(transparent)]
72#[derive(Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]
73pub struct RefStr(str);
74
75impl RefStr {
76 pub fn try_from_str(s: &str) -> Result<&RefStr, check::Error> {
77 TryFrom::try_from(s)
78 }
79
80 #[inline]
81 pub fn as_str(&self) -> &str {
82 self
83 }
84
85 #[inline]
86 pub fn to_ref_string(&self) -> RefString {
87 self.to_owned()
88 }
89
90 pub fn strip_prefix<P>(&self, base: P) -> Option<&RefStr>
91 where
92 P: AsRef<RefStr>,
93 {
94 self._strip_prefix(base.as_ref())
95 }
96
97 fn _strip_prefix(&self, base: &RefStr) -> Option<&RefStr> {
98 self.0
99 .strip_prefix(base.as_str())
100 .and_then(|s| s.strip_prefix('/'))
101 .map(Self::from_str)
102 }
103
104 pub fn join<R>(&self, other: R) -> RefString
109 where
110 R: AsRef<RefStr>,
111 {
112 self._join(other.as_ref())
113 }
114
115 fn _join(&self, other: &RefStr) -> RefString {
116 let mut buf = self.to_ref_string();
117 buf.push(other);
118 buf
119 }
120
121 pub fn to_pattern<P>(&self, pattern: P) -> PatternString
122 where
123 P: AsRef<PatternStr>,
124 {
125 self._to_pattern(pattern.as_ref())
126 }
127
128 fn _to_pattern(&self, pattern: &PatternStr) -> PatternString {
129 self.to_owned().with_pattern(pattern)
130 }
131
132 #[inline]
133 pub fn qualified<'a>(&'a self) -> Option<Qualified<'a>> {
134 Qualified::from_refstr(self)
135 }
136
137 #[inline]
138 pub fn to_namespaced<'a>(&'a self) -> Option<Namespaced<'a>> {
139 self.into()
140 }
141
142 pub fn iter<'a>(&'a self) -> Iter<'a> {
143 self.0.split('/')
144 }
145
146 pub fn components<'a>(&'a self) -> Components<'a> {
147 Components::from(self)
148 }
149
150 pub fn head<'a>(&'a self) -> Component<'a> {
151 self.components().next().expect("`RefStr` cannot be empty")
152 }
153
154 #[cfg(feature = "percent-encoding")]
155 pub fn percent_encode(&self) -> PercentEncode {
156 const FRAGMENT_PERCENT_ENCODE_SET: &percent_encoding::AsciiSet =
158 &percent_encoding::CONTROLS
159 .add(b' ')
160 .add(b'"')
161 .add(b'<')
162 .add(b'>')
163 .add(b'`');
164
165 const PATH_PERCENT_ENCODE_SET: &percent_encoding::AsciiSet = &FRAGMENT_PERCENT_ENCODE_SET
167 .add(b'#')
168 .add(b'?')
169 .add(b'{')
170 .add(b'}');
171
172 percent_encoding::utf8_percent_encode(self.as_str(), PATH_PERCENT_ENCODE_SET)
173 }
174
175 #[cfg(feature = "bstr")]
176 #[inline]
177 pub fn as_bstr(&self) -> &bstr::BStr {
178 self.as_ref()
179 }
180
181 pub(crate) const fn from_str(s: &str) -> &RefStr {
182 unsafe { &*(s as *const str as *const RefStr) }
183 }
184}
185
186impl Deref for RefStr {
187 type Target = str;
188
189 #[inline]
190 fn deref(&self) -> &Self::Target {
191 &self.0
192 }
193}
194
195impl AsRef<str> for RefStr {
196 #[inline]
197 fn as_ref(&self) -> &str {
198 self
199 }
200}
201
202#[cfg(feature = "bstr")]
203impl AsRef<bstr::BStr> for RefStr {
204 #[inline]
205 fn as_ref(&self) -> &bstr::BStr {
206 use bstr::ByteSlice as _;
207 self.as_str().as_bytes().as_bstr()
208 }
209}
210
211impl AsRef<RefStr> for &RefStr {
212 #[inline]
213 fn as_ref(&self) -> &RefStr {
214 self
215 }
216}
217
218impl<'a> TryFrom<&'a str> for &'a RefStr {
219 type Error = check::Error;
220
221 #[inline]
222 fn try_from(s: &'a str) -> Result<Self, Self::Error> {
223 check::ref_format(CHECK_OPTS, s).map(|()| RefStr::from_str(s))
224 }
225}
226
227impl<'a> From<&'a RefStr> for Cow<'a, RefStr> {
228 #[inline]
229 fn from(rs: &'a RefStr) -> Cow<'a, RefStr> {
230 Cow::Borrowed(rs)
231 }
232}
233
234impl Display for RefStr {
235 #[inline]
236 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
237 f.write_str(self)
238 }
239}
240
241#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]
242pub struct RefString(String);
243
244impl RefString {
245 #[inline]
246 pub fn as_refstr(&self) -> &RefStr {
247 self
248 }
249
250 pub fn and<R>(self, other: R) -> Self
256 where
257 R: AsRef<RefStr>,
258 {
259 self._and(other.as_ref())
260 }
261
262 fn _and(mut self, other: &RefStr) -> Self {
263 self.push(other);
264 self
265 }
266
267 pub fn push<R>(&mut self, other: R)
268 where
269 R: AsRef<RefStr>,
270 {
271 self.0.push('/');
272 self.0.push_str(other.as_ref().as_str());
273 }
274
275 #[inline]
276 pub fn pop(&mut self) -> bool {
277 match self.0.rfind('/') {
278 None => false,
279 Some(idx) => {
280 self.0.truncate(idx);
281 true
282 }
283 }
284 }
285
286 pub fn with_pattern<P>(self, pattern: P) -> PatternString
288 where
289 P: AsRef<PatternStr>,
290 {
291 self._with_pattern(pattern.as_ref())
292 }
293
294 fn _with_pattern(self, pattern: &PatternStr) -> PatternString {
295 let mut buf = self.0;
296 buf.push('/');
297 buf.push_str(pattern.as_str());
298
299 PatternString(buf)
300 }
301
302 #[inline]
303 pub fn into_qualified<'a>(self) -> Option<Qualified<'a>> {
304 Qualified::from_refstr(self)
305 }
306
307 #[inline]
308 pub fn reserve(&mut self, additional: usize) {
309 self.0.reserve(additional)
310 }
311
312 #[inline]
313 pub fn shrink_to_fit(&mut self) {
314 self.0.shrink_to_fit()
315 }
316
317 #[cfg(feature = "bstr")]
318 #[inline]
319 pub fn into_bstring(self) -> bstr::BString {
320 self.into()
321 }
322
323 #[cfg(feature = "bstr")]
324 #[inline]
325 pub fn as_bstr(&self) -> &bstr::BStr {
326 self.as_ref()
327 }
328}
329
330impl Deref for RefString {
331 type Target = RefStr;
332
333 #[inline]
334 fn deref(&self) -> &Self::Target {
335 self.borrow()
336 }
337}
338
339impl AsRef<RefStr> for RefString {
340 #[inline]
341 fn as_ref(&self) -> &RefStr {
342 self
343 }
344}
345
346impl AsRef<str> for RefString {
347 #[inline]
348 fn as_ref(&self) -> &str {
349 self.0.as_str()
350 }
351}
352
353#[cfg(feature = "bstr")]
354impl AsRef<bstr::BStr> for RefString {
355 #[inline]
356 fn as_ref(&self) -> &bstr::BStr {
357 use bstr::ByteSlice as _;
358 self.as_str().as_bytes().as_bstr()
359 }
360}
361
362impl Borrow<RefStr> for RefString {
363 #[inline]
364 fn borrow(&self) -> &RefStr {
365 RefStr::from_str(self.0.as_str())
366 }
367}
368
369impl ToOwned for RefStr {
370 type Owned = RefString;
371
372 #[inline]
373 fn to_owned(&self) -> Self::Owned {
374 RefString(self.0.to_owned())
375 }
376}
377
378impl TryFrom<&str> for RefString {
379 type Error = check::Error;
380
381 #[inline]
382 fn try_from(s: &str) -> Result<Self, Self::Error> {
383 RefStr::try_from_str(s).map(ToOwned::to_owned)
384 }
385}
386
387impl TryFrom<String> for RefString {
388 type Error = check::Error;
389
390 #[inline]
391 fn try_from(s: String) -> Result<Self, Self::Error> {
392 check::ref_format(CHECK_OPTS, s.as_str()).map(|()| RefString(s))
393 }
394}
395
396impl<'a> From<&'a RefString> for Cow<'a, RefStr> {
397 #[inline]
398 fn from(rs: &'a RefString) -> Cow<'a, RefStr> {
399 Cow::Borrowed(rs.as_refstr())
400 }
401}
402
403impl<'a> From<RefString> for Cow<'a, RefStr> {
404 #[inline]
405 fn from(rs: RefString) -> Cow<'a, RefStr> {
406 Cow::Owned(rs)
407 }
408}
409
410impl From<RefString> for String {
411 #[inline]
412 fn from(rs: RefString) -> Self {
413 rs.0
414 }
415}
416
417#[cfg(feature = "bstr")]
418impl From<RefString> for bstr::BString {
419 #[inline]
420 fn from(rs: RefString) -> Self {
421 bstr::BString::from(rs.0.into_bytes())
422 }
423}
424
425impl Display for RefString {
426 #[inline]
427 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
428 f.write_str(&self.0)
429 }
430}
431
432impl<A> FromIterator<A> for RefString
433where
434 A: AsRef<RefStr>,
435{
436 fn from_iter<T>(iter: T) -> Self
437 where
438 T: IntoIterator<Item = A>,
439 {
440 let mut buf = String::new();
441 for c in iter {
442 buf.push_str(c.as_ref().as_str());
443 buf.push('/');
444 }
445 assert!(!buf.is_empty(), "empty iterator");
446 buf.truncate(buf.len() - 1);
447
448 Self(buf)
449 }
450}
451
452impl<A> Extend<A> for RefString
453where
454 A: AsRef<RefStr>,
455{
456 fn extend<T>(&mut self, iter: T)
457 where
458 T: IntoIterator<Item = A>,
459 {
460 for x in iter {
461 self.push(x)
462 }
463 }
464}