1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
use bstr::BStr;
pub mod name {
use bstr::BString;
use quick_error::quick_error;
quick_error! {
#[derive(Debug)]
#[allow(missing_docs)]
pub enum Error {
InvalidByte(name: BString) {
display("A ref must not contain invalid bytes or ascii control characters: '{}'", name)
}
DoubleDot {
display("A ref must not contain '..' as it may be mistaken for a range")
}
LockFileSuffix {
display("A ref must not end with '.lock'")
}
ReflogPortion {
display("A ref must not contain '@{{' which is a part of a ref-log")
}
Asterisk {
display("A ref must not contain '*' character")
}
StartsWithDot {
display("A ref must not start with a '.'")
}
EndsWithSlash {
display("A ref must not end with a '/'")
}
Empty {
display("A ref must not be empty")
}
}
}
}
pub fn name(bytes: &BStr) -> Result<&BStr, name::Error> {
if bytes.is_empty() {
return Err(name::Error::Empty);
}
if *bytes.last().expect("non-empty") == b'/' {
return Err(name::Error::EndsWithSlash);
}
let mut previous = 0;
for byte in bytes.iter() {
match byte {
b'\\' | b'^' | b':' | b'[' | b'?' | b' ' | b'~' | b'\0'..=b'\x1F' | b'\x7F' => {
return Err(name::Error::InvalidByte((&[*byte][..]).into()))
}
b'*' => return Err(name::Error::Asterisk),
b'.' if previous == b'.' => return Err(name::Error::DoubleDot),
b'{' if previous == b'@' => return Err(name::Error::ReflogPortion),
_ => {}
}
previous = *byte;
}
if bytes[0] == b'.' {
return Err(name::Error::StartsWithDot);
}
if bytes.ends_with(b".lock") {
return Err(name::Error::LockFileSuffix);
}
Ok(bytes)
}