git_ref/store/packed/
iter.rs1use git_object::bstr::{BString, ByteSlice};
2
3use crate::store_impl::{packed, packed::decode};
4
5impl packed::Buffer {
7 pub fn iter(&self) -> Result<packed::Iter<'_>, packed::iter::Error> {
13 packed::Iter::new(self.as_ref())
14 }
15
16 pub fn iter_prefixed(&self, prefix: impl Into<BString>) -> Result<packed::Iter<'_>, packed::iter::Error> {
18 let prefix = prefix.into();
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 match decode::reference::<()>(self.cursor) {
33 Ok((rest, reference)) => {
34 self.cursor = rest;
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 let (failed_line, next_cursor) = self
46 .cursor
47 .find_byte(b'\n')
48 .map_or((self.cursor, &[][..]), |pos| self.cursor.split_at(pos + 1));
49 self.cursor = next_cursor;
50 let line_number = self.current_line;
51 self.current_line += 1;
52
53 Some(Err(Error::Reference {
54 invalid_line: failed_line
55 .get(..failed_line.len().saturating_sub(1))
56 .unwrap_or(failed_line)
57 .into(),
58 line_number,
59 }))
60 }
61 }
62 }
63}
64
65impl<'a> packed::Iter<'a> {
66 pub fn new(packed: &'a [u8]) -> Result<Self, Error> {
68 Self::new_with_prefix(packed, None)
69 }
70
71 pub(in crate::store_impl::packed) fn new_with_prefix(
75 packed: &'a [u8],
76 prefix: Option<BString>,
77 ) -> Result<Self, Error> {
78 if packed.is_empty() {
79 Ok(packed::Iter {
80 cursor: packed,
81 prefix,
82 current_line: 1,
83 })
84 } else if packed[0] == b'#' {
85 let (refs, _header) = decode::header::<()>(packed).map_err(|_| Error::Header {
86 invalid_first_line: packed.lines().next().unwrap_or(packed).into(),
87 })?;
88 Ok(packed::Iter {
89 cursor: refs,
90 prefix,
91 current_line: 2,
92 })
93 } else {
94 Ok(packed::Iter {
95 cursor: packed,
96 prefix,
97 current_line: 1,
98 })
99 }
100 }
101}
102
103mod error {
104 use git_object::bstr::BString;
105
106 #[derive(Debug, thiserror::Error)]
108 #[allow(missing_docs)]
109 pub enum Error {
110 #[error("The header existed but could not be parsed: {invalid_first_line:?}")]
111 Header { invalid_first_line: BString },
112 #[error("Invalid reference in line {line_number}: {invalid_line:?}")]
113 Reference { invalid_line: BString, line_number: usize },
114 }
115}
116
117pub use error::Error;