gix_ref/store/file/loose/reference/
decode.rs1use gix_hash::ObjectId;
2use gix_object::bstr::BString;
3
4use crate::{parse::hex_hash, store_impl::file::loose::Reference, FullName, Target};
5
6enum MaybeUnsafeState {
7 Id(ObjectId),
8 UnvalidatedPath(BString),
9}
10
11#[derive(Debug, thiserror::Error)]
13#[allow(missing_docs)]
14pub enum Error {
15 #[error("{content:?} could not be parsed")]
16 Parse { content: BString },
17 #[error("The path {path:?} to a symbolic reference within a ref file is invalid")]
18 RefnameValidation {
19 source: gix_validate::reference::name::Error,
20 path: BString,
21 },
22}
23
24impl TryFrom<MaybeUnsafeState> for Target {
25 type Error = Error;
26
27 fn try_from(v: MaybeUnsafeState) -> Result<Self, Self::Error> {
28 Ok(match v {
29 MaybeUnsafeState::Id(id) => Target::Object(id),
30 MaybeUnsafeState::UnvalidatedPath(name) => {
31 Target::Symbolic(match gix_validate::reference::name(name.as_ref()) {
32 Ok(_) => FullName(name),
33 Err(err) => {
34 return Err(Error::RefnameValidation {
35 source: err,
36 path: name,
37 })
38 }
39 })
40 }
41 })
42 }
43}
44
45impl Reference {
46 pub fn try_from_path(name: FullName, path_contents: &[u8], hash_kind: gix_hash::Kind) -> Result<Self, Error> {
49 Ok(Reference {
50 name,
51 target: parse(path_contents, hash_kind)
52 .map_err(|_| Error::Parse {
53 content: path_contents.into(),
54 })?
55 .try_into()?,
56 })
57 }
58}
59
60fn parse(mut i: &[u8], hash_kind: gix_hash::Kind) -> Result<MaybeUnsafeState, ()> {
72 if let Some(rest) = i.strip_prefix(b"ref: ") {
73 i = rest;
74 while i.first() == Some(&b' ') {
75 i = &i[1..];
76 }
77 let path_end = i.iter().position(|b| *b == b'\r' || *b == b'\n').unwrap_or(i.len());
78 let path = i[..path_end].into();
79 Ok(MaybeUnsafeState::UnvalidatedPath(path))
80 } else {
81 let hex = hex_hash(&mut i, hash_kind)?;
82 if i.first().is_some_and(u8::is_ascii_hexdigit) {
83 return Err(());
84 }
85 Ok(MaybeUnsafeState::Id(ObjectId::from_hex(hex).expect("prior validation")))
86 }
87}