gix_ref/store/file/loose/reference/
decode.rs

1use gix_hash::ObjectId;
2use gix_object::bstr::BString;
3use winnow::{
4    combinator::{opt, terminated},
5    prelude::*,
6    token::take_while,
7};
8
9use crate::{
10    parse::{hex_hash, newline},
11    store_impl::file::loose::Reference,
12    FullName, Target,
13};
14
15enum MaybeUnsafeState {
16    Id(ObjectId),
17    UnvalidatedPath(BString),
18}
19
20/// The error returned by [`Reference::try_from_path()`].
21#[derive(Debug, thiserror::Error)]
22#[allow(missing_docs)]
23pub enum Error {
24    #[error("{content:?} could not be parsed")]
25    Parse { content: BString },
26    #[error("The path {path:?} to a symbolic reference within a ref file is invalid")]
27    RefnameValidation {
28        source: gix_validate::reference::name::Error,
29        path: BString,
30    },
31}
32
33impl TryFrom<MaybeUnsafeState> for Target {
34    type Error = Error;
35
36    fn try_from(v: MaybeUnsafeState) -> Result<Self, Self::Error> {
37        Ok(match v {
38            MaybeUnsafeState::Id(id) => Target::Object(id),
39            MaybeUnsafeState::UnvalidatedPath(name) => {
40                Target::Symbolic(match gix_validate::reference::name(name.as_ref()) {
41                    Ok(_) => FullName(name),
42                    Err(err) => {
43                        return Err(Error::RefnameValidation {
44                            source: err,
45                            path: name,
46                        })
47                    }
48                })
49            }
50        })
51    }
52}
53
54impl Reference {
55    /// Create a new reference of the given `parent` store with `relative_path` service as unique identifier
56    /// at which the `path_contents` was read to obtain the refs value.
57    pub fn try_from_path(name: FullName, mut path_contents: &[u8]) -> Result<Self, Error> {
58        Ok(Reference {
59            name,
60            target: parse(&mut path_contents)
61                .map_err(|_| Error::Parse {
62                    content: path_contents.into(),
63                })?
64                .try_into()?,
65        })
66    }
67}
68
69fn parse(i: &mut &[u8]) -> PResult<MaybeUnsafeState> {
70    if let Some(_ref_prefix) = opt(terminated("ref: ", take_while(0.., b' '))).parse_next(i)? {
71        terminated(take_while(0.., |b| b != b'\r' && b != b'\n'), opt(newline))
72            .map(|path| MaybeUnsafeState::UnvalidatedPath(path.into()))
73            .parse_next(i)
74    } else {
75        terminated(hex_hash, opt(newline))
76            .map(|hex| MaybeUnsafeState::Id(ObjectId::from_hex(hex).expect("prior validation")))
77            .parse_next(i)
78    }
79}