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