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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
//! Various functionality related to git references
#![forbid(unsafe_code)]
#![deny(rust_2018_idioms)]
#![allow(missing_docs)]

pub mod reference {
    pub mod name {
        use quick_error::quick_error;

        quick_error! {
            #[derive(Debug)]
            pub enum Error {
                Todo
            }
        }
    }

    use bstr::BStr;
    pub fn name(path: &BStr) -> Result<&BStr, name::Error> {
        Ok(path)
    }
}
pub use reference::name as refname;

pub mod tag {
    use bstr::BStr;
    ///
    pub mod name {
        use bstr::BString;
        use quick_error::quick_error;

        quick_error! {
            /// The error returned by [`name()`]
            #[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")
                }
            }
        }
    }

    /// Assure the given `bytes` resemble a valid git ref name, which are returned unchanged on success.
    pub fn name(bytes: &BStr) -> Result<&BStr, name::Error> {
        if bytes.is_empty() {
            return Err(name::Error::Empty);
        }

        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(bytes.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.last().expect("non-empty") == b'/' {
            return Err(name::Error::EndsWithSlash);
        }
        if bytes.ends_with(b".lock") {
            return Err(name::Error::LockFileSuffix);
        }
        Ok(bytes)
    }
}
pub use tag::name as tagname;