git_ref_format_core/
check.rsuse thiserror::Error;
pub struct Options {
pub allow_onelevel: bool,
pub allow_pattern: bool,
}
#[derive(Debug, PartialEq, Eq, Error)]
#[non_exhaustive]
pub enum Error {
#[error("empty input")]
Empty,
#[error("lone '@' character")]
LoneAt,
#[error("consecutive or trailing slash")]
Slash,
#[error("ends with '.lock'")]
DotLock,
#[error("consecutive dots ('..')")]
DotDot,
#[error("at-open-brace ('@{{')")]
AtOpenBrace,
#[error("invalid character {0:?}")]
InvalidChar(char),
#[error("component starts with '.'")]
StartsDot,
#[error("component ends with '.'")]
EndsDot,
#[error("control character")]
Control,
#[error("whitespace")]
Space,
#[error("must contain at most one '*'")]
Pattern,
#[error("must contain at least one '/'")]
OneLevel,
}
pub fn ref_format(opts: Options, s: &str) -> Result<(), Error> {
match s {
"" => Err(Error::Empty),
"@" => Err(Error::LoneAt),
"." => Err(Error::StartsDot),
_ => {
let mut globs = 0usize;
let mut parts = 0usize;
for x in s.split('/') {
if x.is_empty() {
return Err(Error::Slash);
}
parts += 1;
if x.ends_with(".lock") {
return Err(Error::DotLock);
}
let last_char = x.chars().count() - 1;
for (i, y) in x.chars().zip(x.chars().cycle().skip(1)).enumerate() {
match y {
('.', '.') => return Err(Error::DotDot),
('@', '{') => return Err(Error::AtOpenBrace),
('\0', _) => return Err(Error::InvalidChar('\0')),
('\\', _) => return Err(Error::InvalidChar('\\')),
('~', _) => return Err(Error::InvalidChar('~')),
('^', _) => return Err(Error::InvalidChar('^')),
(':', _) => return Err(Error::InvalidChar(':')),
('?', _) => return Err(Error::InvalidChar('?')),
('[', _) => return Err(Error::InvalidChar('[')),
('*', _) => globs += 1,
('.', _) if i == 0 => return Err(Error::StartsDot),
('.', _) if i == last_char => return Err(Error::EndsDot),
(' ', _) => return Err(Error::Space),
(z, _) if z.is_ascii_control() => return Err(Error::Control),
_ => continue,
}
}
}
if parts < 2 && !opts.allow_onelevel {
Err(Error::OneLevel)
} else if globs > 1 && opts.allow_pattern {
Err(Error::Pattern)
} else if globs > 0 && !opts.allow_pattern {
Err(Error::InvalidChar('*'))
} else {
Ok(())
}
}
}
}