gix_validate/
reference.rs

1use bstr::{BStr, BString, ByteSlice};
2
3///
4pub mod name {
5    use std::convert::Infallible;
6
7    /// The error used in [name()][super::name()] and [`name_partial()`][super::name_partial()]
8    #[derive(Debug, thiserror::Error)]
9    #[allow(missing_docs)]
10    pub enum Error {
11        #[error("A reference must be a valid tag name as well")]
12        Tag(#[from] crate::tag::name::Error),
13        #[error("Standalone references must be all uppercased, like 'HEAD'")]
14        SomeLowercase,
15    }
16
17    impl From<Infallible> for Error {
18        fn from(_: Infallible) -> Self {
19            unreachable!("this impl is needed to allow passing a known valid partial path as parameter")
20        }
21    }
22}
23
24/// Validate a reference name running all the tests in the book. This disallows lower-case references like `lower`, but also allows
25/// ones like `HEAD`, and `refs/lower`.
26pub fn name(path: &BStr) -> Result<&BStr, name::Error> {
27    match validate(path, Mode::Complete)? {
28        None => Ok(path),
29        Some(_) => {
30            unreachable!("Without sanitization, there is no chance a sanitized version is returned.")
31        }
32    }
33}
34
35/// Validate a partial reference name. As it is assumed to be partial, names like `some-name` is allowed
36/// even though these would be disallowed with when using [`name()`].
37pub fn name_partial(path: &BStr) -> Result<&BStr, name::Error> {
38    match validate(path, Mode::Partial)? {
39        None => Ok(path),
40        Some(_) => {
41            unreachable!("Without sanitization, there is no chance a sanitized version is returned.")
42        }
43    }
44}
45
46/// The infallible version of [`name_partial()`] which instead of failing, alters `path` and returns it to be a valid
47/// partial name, which would also pass [`name_partial()`].
48///
49/// Note that an empty `path` is replaced with a `-` in order to be valid.
50pub fn name_partial_or_sanitize(path: &BStr) -> BString {
51    validate(path, Mode::PartialSanitize)
52        .expect("BUG: errors cannot happen as any issue is fixed instantly")
53        .expect("we always rebuild the path")
54}
55
56enum Mode {
57    Complete,
58    Partial,
59    /// like Partial, but instead of failing, a sanitized version is returned.
60    PartialSanitize,
61}
62
63fn validate(path: &BStr, mode: Mode) -> Result<Option<BString>, name::Error> {
64    let out = crate::tag::name_inner(
65        path,
66        match mode {
67            Mode::Complete | Mode::Partial => crate::tag::Mode::Validate,
68            Mode::PartialSanitize => crate::tag::Mode::Sanitize,
69        },
70    )?;
71    if let Mode::Complete = mode {
72        let input = out.as_ref().map_or(path, |b| b.as_bstr());
73        let saw_slash = input.find_byte(b'/').is_some();
74        if !saw_slash && !input.iter().all(|c| c.is_ascii_uppercase() || *c == b'_') {
75            return Err(name::Error::SomeLowercase);
76        }
77    }
78    Ok(out)
79}