]>
Commit | Line | Data |
---|---|---|
0a29b90c FG |
1 | use bstr::BStr; |
2 | use gix_hash::{oid, ObjectId}; | |
781aab86 FG |
3 | use winnow::{ |
4 | combinator::{eof, opt, terminated}, | |
5 | error::{ParserError, StrContext}, | |
6 | prelude::*, | |
7 | stream::AsChar, | |
8 | token::take_while, | |
0a29b90c FG |
9 | }; |
10 | ||
11 | use crate::{bstr::ByteSlice, parse, parse::NL, tag::decode, Kind, TagRefIter}; | |
12 | ||
49aad941 | 13 | #[derive(Default, Copy, Clone)] |
0a29b90c | 14 | pub(crate) enum State { |
49aad941 | 15 | #[default] |
0a29b90c FG |
16 | Target, |
17 | TargetKind, | |
18 | Name, | |
19 | Tagger, | |
20 | Message, | |
21 | } | |
22 | ||
0a29b90c FG |
23 | impl<'a> TagRefIter<'a> { |
24 | /// Create a tag iterator from data. | |
25 | pub fn from_bytes(data: &'a [u8]) -> TagRefIter<'a> { | |
26 | TagRefIter { | |
27 | data, | |
28 | state: State::default(), | |
29 | } | |
30 | } | |
31 | ||
32 | /// Returns the target id of this tag if it is the first function called and if there is no error in decoding | |
33 | /// the data. | |
34 | /// | |
35 | /// Note that this method must only be called once or else will always return None while consuming a single token. | |
36 | /// Errors are coerced into options, hiding whether there was an error or not. The caller should assume an error if they | |
37 | /// call the method as intended. Such a squelched error cannot be recovered unless the objects data is retrieved and parsed again. | |
38 | /// `next()`. | |
39 | pub fn target_id(mut self) -> Result<ObjectId, crate::decode::Error> { | |
40 | let token = self.next().ok_or_else(missing_field)??; | |
41 | Token::into_id(token).ok_or_else(missing_field) | |
42 | } | |
43 | ||
44 | /// Returns the taggers signature if there is no decoding error, and if this field exists. | |
45 | /// Errors are coerced into options, hiding whether there was an error or not. The caller knows if there was an error or not. | |
46 | pub fn tagger(mut self) -> Result<Option<gix_actor::SignatureRef<'a>>, crate::decode::Error> { | |
47 | self.find_map(|t| match t { | |
48 | Ok(Token::Tagger(signature)) => Some(Ok(signature)), | |
49 | Err(err) => Some(Err(err)), | |
50 | _ => None, | |
51 | }) | |
52 | .ok_or_else(missing_field)? | |
53 | } | |
54 | } | |
55 | ||
56 | fn missing_field() -> crate::decode::Error { | |
57 | crate::decode::empty_error() | |
58 | } | |
59 | ||
60 | impl<'a> TagRefIter<'a> { | |
781aab86 | 61 | #[inline] |
4b012472 FG |
62 | fn next_inner(mut i: &'a [u8], state: &mut State) -> Result<(&'a [u8], Token<'a>), crate::decode::Error> { |
63 | let input = &mut i; | |
64 | match Self::next_inner_(input, state) { | |
65 | Ok(token) => Ok((*input, token)), | |
66 | Err(err) => Err(crate::decode::Error::with_err(err, input)), | |
67 | } | |
781aab86 FG |
68 | } |
69 | ||
70 | fn next_inner_( | |
4b012472 | 71 | input: &mut &'a [u8], |
781aab86 | 72 | state: &mut State, |
4b012472 | 73 | ) -> Result<Token<'a>, winnow::error::ErrMode<crate::decode::ParseError>> { |
0a29b90c FG |
74 | use State::*; |
75 | Ok(match state { | |
76 | Target => { | |
781aab86 FG |
77 | let target = (|i: &mut _| parse::header_field(i, b"object", parse::hex_hash)) |
78 | .context(StrContext::Expected("object <40 lowercase hex char>".into())) | |
4b012472 | 79 | .parse_next(input)?; |
0a29b90c | 80 | *state = TargetKind; |
4b012472 FG |
81 | Token::Target { |
82 | id: ObjectId::from_hex(target).expect("parsing validation"), | |
83 | } | |
0a29b90c FG |
84 | } |
85 | TargetKind => { | |
781aab86 FG |
86 | let kind = (|i: &mut _| parse::header_field(i, b"type", take_while(1.., AsChar::is_alpha))) |
87 | .context(StrContext::Expected("type <object kind>".into())) | |
4b012472 | 88 | .parse_next(input)?; |
781aab86 | 89 | let kind = Kind::from_bytes(kind) |
4b012472 | 90 | .map_err(|_| winnow::error::ErrMode::from_error_kind(input, winnow::error::ErrorKind::Verify))?; |
0a29b90c | 91 | *state = Name; |
4b012472 | 92 | Token::TargetKind(kind) |
0a29b90c FG |
93 | } |
94 | Name => { | |
781aab86 FG |
95 | let tag_version = (|i: &mut _| parse::header_field(i, b"tag", take_while(1.., |b| b != NL[0]))) |
96 | .context(StrContext::Expected("tag <version>".into())) | |
4b012472 | 97 | .parse_next(input)?; |
0a29b90c | 98 | *state = Tagger; |
4b012472 | 99 | Token::Name(tag_version.as_bstr()) |
0a29b90c FG |
100 | } |
101 | Tagger => { | |
781aab86 FG |
102 | let signature = opt(|i: &mut _| parse::header_field(i, b"tagger", parse::signature)) |
103 | .context(StrContext::Expected("tagger <signature>".into())) | |
4b012472 | 104 | .parse_next(input)?; |
0a29b90c | 105 | *state = Message; |
4b012472 | 106 | Token::Tagger(signature) |
0a29b90c FG |
107 | } |
108 | Message => { | |
4b012472 | 109 | let (message, pgp_signature) = terminated(decode::message, eof).parse_next(input)?; |
0a29b90c | 110 | debug_assert!( |
4b012472 | 111 | input.is_empty(), |
0a29b90c FG |
112 | "we should have consumed all data - otherwise iter may go forever" |
113 | ); | |
4b012472 | 114 | Token::Body { message, pgp_signature } |
0a29b90c FG |
115 | } |
116 | }) | |
117 | } | |
118 | } | |
119 | ||
120 | impl<'a> Iterator for TagRefIter<'a> { | |
121 | type Item = Result<Token<'a>, crate::decode::Error>; | |
122 | ||
123 | fn next(&mut self) -> Option<Self::Item> { | |
124 | if self.data.is_empty() { | |
125 | return None; | |
126 | } | |
127 | match Self::next_inner(self.data, &mut self.state) { | |
128 | Ok((data, token)) => { | |
129 | self.data = data; | |
130 | Some(Ok(token)) | |
131 | } | |
132 | Err(err) => { | |
133 | self.data = &[]; | |
134 | Some(Err(err)) | |
135 | } | |
136 | } | |
137 | } | |
138 | } | |
139 | ||
140 | /// A token returned by the [tag iterator][TagRefIter]. | |
141 | #[allow(missing_docs)] | |
142 | #[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)] | |
143 | pub enum Token<'a> { | |
144 | Target { | |
145 | id: ObjectId, | |
146 | }, | |
147 | TargetKind(Kind), | |
148 | Name(&'a BStr), | |
149 | Tagger(Option<gix_actor::SignatureRef<'a>>), | |
150 | Body { | |
151 | message: &'a BStr, | |
152 | pgp_signature: Option<&'a BStr>, | |
153 | }, | |
154 | } | |
155 | ||
156 | impl<'a> Token<'a> { | |
157 | /// Return the object id of this token if its a [Target][Token::Target]. | |
158 | pub fn id(&self) -> Option<&oid> { | |
159 | match self { | |
160 | Token::Target { id } => Some(id.as_ref()), | |
161 | _ => None, | |
162 | } | |
163 | } | |
164 | ||
165 | /// Return the owned object id of this token if its a [Target][Token::Target]. | |
166 | pub fn into_id(self) -> Option<ObjectId> { | |
167 | match self { | |
168 | Token::Target { id } => Some(id), | |
169 | _ => None, | |
170 | } | |
171 | } | |
172 | } |