gix_ref/store/packed/
iter.rs1use gix_object::bstr::{BString, ByteSlice};
2use winnow::{
3 combinator::{preceded, rest},
4 prelude::*,
5 stream::Stream as _,
6};
7
8use crate::store_impl::{packed, packed::decode};
9
10impl packed::Buffer {
12 pub fn iter(&self) -> Result<packed::Iter<'_>, packed::iter::Error> {
18 packed::Iter::new(self.as_ref())
19 }
20
21 pub fn iter_prefixed(&self, prefix: BString) -> Result<packed::Iter<'_>, packed::iter::Error> {
23 let first_record_with_prefix = self.binary_search_by(prefix.as_bstr()).unwrap_or_else(|(_, pos)| pos);
24 packed::Iter::new_with_prefix(&self.as_ref()[first_record_with_prefix..], Some(prefix))
25 }
26}
27
28impl<'a> Iterator for packed::Iter<'a> {
29 type Item = Result<packed::Reference<'a>, Error>;
30
31 fn next(&mut self) -> Option<Self::Item> {
32 if self.cursor.is_empty() {
33 return None;
34 }
35
36 let start = self.cursor.checkpoint();
37 match decode::reference::<()>.parse_next(&mut self.cursor) {
38 Ok(reference) => {
39 self.current_line += 1;
40 if let Some(ref prefix) = self.prefix {
41 if !reference.name.as_bstr().starts_with_str(prefix) {
42 self.cursor = &[];
43 return None;
44 }
45 }
46 Some(Ok(reference))
47 }
48 Err(_) => {
49 self.cursor.reset(&start);
50 let (failed_line, next_cursor) = self
51 .cursor
52 .find_byte(b'\n')
53 .map_or((self.cursor, &[][..]), |pos| self.cursor.split_at(pos + 1));
54 self.cursor = next_cursor;
55 let line_number = self.current_line;
56 self.current_line += 1;
57
58 Some(Err(Error::Reference {
59 invalid_line: failed_line
60 .get(..failed_line.len().saturating_sub(1))
61 .unwrap_or(failed_line)
62 .into(),
63 line_number,
64 }))
65 }
66 }
67 }
68}
69
70impl<'a> packed::Iter<'a> {
71 pub fn new(packed: &'a [u8]) -> Result<Self, Error> {
73 Self::new_with_prefix(packed, None)
74 }
75
76 pub(in crate::store_impl::packed) fn new_with_prefix(
80 packed: &'a [u8],
81 prefix: Option<BString>,
82 ) -> Result<Self, Error> {
83 if packed.is_empty() {
84 Ok(packed::Iter {
85 cursor: packed,
86 prefix,
87 current_line: 1,
88 })
89 } else if packed[0] == b'#' {
90 let mut input = packed;
91 let refs = preceded(decode::header::<()>, rest)
92 .parse_next(&mut input)
93 .map_err(|_| Error::Header {
94 invalid_first_line: packed.lines().next().unwrap_or(packed).into(),
95 })?;
96 Ok(packed::Iter {
97 cursor: refs,
98 prefix,
99 current_line: 2,
100 })
101 } else {
102 Ok(packed::Iter {
103 cursor: packed,
104 prefix,
105 current_line: 1,
106 })
107 }
108 }
109}
110
111mod error {
112 use gix_object::bstr::BString;
113
114 #[derive(Debug, thiserror::Error)]
116 #[allow(missing_docs)]
117 pub enum Error {
118 #[error("The header existed but could not be parsed: {invalid_first_line:?}")]
119 Header { invalid_first_line: BString },
120 #[error("Invalid reference in line {line_number}: {invalid_line:?}")]
121 Reference { invalid_line: BString, line_number: usize },
122 }
123}
124
125pub use error::Error;