git_ref/store/packed/
decode.rs

1use std::convert::TryInto;
2
3use git_object::bstr::{BStr, ByteSlice};
4use nom::{
5    bytes::complete::{tag, take_while},
6    combinator::{map, map_res, opt},
7    error::{FromExternalError, ParseError},
8    sequence::{delimited, preceded, terminated, tuple},
9    IResult,
10};
11
12use crate::{
13    parse::{hex_hash, newline},
14    store_impl::packed,
15};
16
17#[derive(Debug, PartialEq, Eq)]
18enum Peeled {
19    Unspecified,
20    Partial,
21    Fully,
22}
23
24/// Information parsed from the header of a packed ref file
25#[derive(Debug, PartialEq, Eq)]
26pub struct Header {
27    peeled: Peeled,
28    pub sorted: bool,
29}
30
31impl Default for Header {
32    fn default() -> Self {
33        Header {
34            peeled: Peeled::Unspecified,
35            sorted: false,
36        }
37    }
38}
39
40fn until_newline<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], &'a BStr, E>
41where
42    E: ParseError<&'a [u8]>,
43{
44    map(
45        terminated(take_while(|b: u8| b != b'\r' && b != b'\n'), newline),
46        |not_newline| not_newline.as_bstr(),
47    )(input)
48}
49
50pub fn header<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], Header, E>
51where
52    E: ParseError<&'a [u8]>,
53{
54    let (rest, traits) = preceded(tag(b"# pack-refs with: "), until_newline)(input)?;
55
56    let mut peeled = Peeled::Unspecified;
57    let mut sorted = false;
58    for token in traits.as_bstr().split_str(b" ") {
59        if token == b"fully-peeled" {
60            peeled = Peeled::Fully;
61        } else if token == b"peeled" {
62            peeled = Peeled::Partial;
63        } else if token == b"sorted" {
64            sorted = true;
65        }
66    }
67
68    Ok((rest, Header { peeled, sorted }))
69}
70
71pub fn reference<'a, E: ParseError<&'a [u8]> + FromExternalError<&'a [u8], crate::name::Error>>(
72    input: &'a [u8],
73) -> IResult<&'a [u8], packed::Reference<'a>, E> {
74    let (input, (target, name)) = tuple((
75        terminated(hex_hash, tag(b" ")),
76        map_res(until_newline, TryInto::try_into),
77    ))(input)?;
78    let (rest, object) = opt(delimited(tag(b"^"), hex_hash, newline))(input)?;
79    Ok((rest, packed::Reference { name, target, object }))
80}
81
82#[cfg(test)]
83mod tests;