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
use bstr::{BStr, BString}; use quick_error::quick_error; quick_error! { #[derive(Debug)] pub enum NameError { 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(name: &BStr) -> Result<&BStr, NameError> { if name.is_empty() { return Err(NameError::Empty); } let mut last = 0; for byte in name.iter() { match byte { b'\\' | b'^' | b':' | b'[' | b'?' | b' ' | b'~' | b'\0'..=b'\x1F' | b'\x7F' => { return Err(NameError::InvalidByte(name.into())) } b'*' => return Err(NameError::Asterisk), b'.' if last == b'.' => return Err(NameError::DoubleDot), b'{' if last == b'@' => return Err(NameError::ReflogPortion), _ => {} } last = *byte; } if name[0] == b'.' { return Err(NameError::StartsWithDot); } if *name.last().expect("non-empty") == b'/' { return Err(NameError::EndsWithSlash); } if name.ends_with(b".lock") { return Err(NameError::LockFileSuffix); } Ok(name) }