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