gix_ref/store/file/loose/reference/
decode.rs1use 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#[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 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}