git_async/object/
header.rs1use crate::{parsing::ParseResult, subslice_range::SubsliceRange};
2use accessory::Accessors;
3use alloc::vec::Vec;
4use core::iter::FusedIterator;
5use core::ops::Range;
6use nom::{
7 Parser,
8 bytes::complete::take_till,
9 character::complete::{char, newline},
10 combinator::{not, peek},
11 multi::many0,
12 sequence::{delimited, terminated},
13};
14
15#[derive(Clone, PartialEq, Eq, Accessors)]
17pub struct ObjectHeader<'a> {
18 #[access(get(cp))]
20 name: &'a [u8],
21 #[access(get(cp))]
25 value: &'a [u8],
26}
27
28#[derive(Clone)]
29pub(crate) struct RangeObjectHeader {
30 pub(crate) name: Range<usize>,
31 pub(crate) value: Range<usize>,
32}
33
34impl RangeObjectHeader {
35 pub(crate) fn parser(input: &[u8]) -> ParseResult<&[u8], Vec<Self>> {
36 let header = (
37 delimited(peek(not(newline)), take_till(|c| c == b' '), char(' ')),
38 continued_line,
39 );
40 let mut p = terminated(many0(header), newline);
41 let (rest, raw_headers) = p.parse(input)?;
42 Ok((
43 rest,
44 raw_headers
45 .into_iter()
46 .map(|(name, value)| RangeObjectHeader {
47 name: input.subslice_range_stable(name).unwrap(),
48 value: input.subslice_range_stable(value).unwrap(),
49 })
50 .collect(),
51 ))
52 }
53}
54
55pub struct ObjectHeaderIter<'a> {
57 body: &'a [u8],
58 headers: &'a [RangeObjectHeader],
59 pos: usize,
60}
61
62impl<'a> ObjectHeaderIter<'a> {
63 pub(crate) fn new(body: &'a [u8], headers: &'a [RangeObjectHeader]) -> Self {
64 Self {
65 body,
66 headers,
67 pos: 0,
68 }
69 }
70}
71
72impl<'a> Iterator for ObjectHeaderIter<'a> {
73 type Item = ObjectHeader<'a>;
74
75 fn next(&mut self) -> Option<Self::Item> {
76 let header = self.headers.get(self.pos)?;
77 self.pos += 1;
78 Some(ObjectHeader {
79 name: &self.body[header.name.clone()],
80 value: &self.body[header.value.clone()],
81 })
82 }
83
84 fn size_hint(&self) -> (usize, Option<usize>) {
85 (
86 self.headers.len() - self.pos,
87 Some(self.headers.len() - self.pos),
88 )
89 }
90}
91
92impl FusedIterator for ObjectHeaderIter<'_> {}
93impl ExactSizeIterator for ObjectHeaderIter<'_> {}
94
95#[allow(clippy::unnecessary_wraps)]
96fn continued_line(input: &[u8]) -> ParseResult<&[u8], &[u8]> {
97 let mut slice_pos = input.len();
98 for (pos, window) in input.windows(2).enumerate() {
99 if window[0] == b'\n' && window[1] != b' ' {
100 slice_pos = pos;
101 break;
102 }
103 }
104 Ok((&input[(slice_pos + 1)..], &input[..slice_pos]))
105}
106
107#[cfg(test)]
108mod tests {
109 use super::*;
110
111 #[test]
112 fn parse_continued_line() {
113 let data = b"some stuff\n continuation\nnext line";
114 let (rest, parsed) = continued_line.parse(data).unwrap();
115 assert_eq!(parsed, b"some stuff\n continuation");
116 assert_eq!(rest, b"next line");
117 }
118}