gix_object/tag/
ref_iter.rs1use bstr::BStr;
2use gix_hash::{oid, ObjectId};
3use winnow::{
4 combinator::{eof, opt, terminated},
5 error::{ParserError, StrContext},
6 prelude::*,
7 stream::AsChar,
8 token::take_while,
9};
10
11use crate::{bstr::ByteSlice, parse, parse::NL, tag::decode, Kind, TagRefIter};
12
13#[derive(Default, Copy, Clone)]
14pub(crate) enum State {
15 #[default]
16 Target,
17 TargetKind,
18 Name,
19 Tagger,
20 Message,
21}
22
23impl<'a> TagRefIter<'a> {
24 pub fn from_bytes(data: &'a [u8]) -> TagRefIter<'a> {
26 TagRefIter {
27 data,
28 state: State::default(),
29 }
30 }
31
32 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 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
56fn missing_field() -> crate::decode::Error {
57 crate::decode::empty_error()
58}
59
60impl<'a> TagRefIter<'a> {
61 #[inline]
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 }
68 }
69
70 fn next_inner_(
71 input: &mut &'a [u8],
72 state: &mut State,
73 ) -> Result<Token<'a>, winnow::error::ErrMode<crate::decode::ParseError>> {
74 use State::*;
75 Ok(match state {
76 Target => {
77 let target = (|i: &mut _| parse::header_field(i, b"object", parse::hex_hash))
78 .context(StrContext::Expected("object <40 lowercase hex char>".into()))
79 .parse_next(input)?;
80 *state = TargetKind;
81 Token::Target {
82 id: ObjectId::from_hex(target).expect("parsing validation"),
83 }
84 }
85 TargetKind => {
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()))
88 .parse_next(input)?;
89 let kind = Kind::from_bytes(kind).map_err(|_| winnow::error::ErrMode::from_input(input))?;
90 *state = Name;
91 Token::TargetKind(kind)
92 }
93 Name => {
94 let tag_version = (|i: &mut _| parse::header_field(i, b"tag", take_while(1.., |b| b != NL[0])))
95 .context(StrContext::Expected("tag <version>".into()))
96 .parse_next(input)?;
97 *state = Tagger;
98 Token::Name(tag_version.as_bstr())
99 }
100 Tagger => {
101 let signature = opt(|i: &mut _| parse::header_field(i, b"tagger", parse::signature))
102 .context(StrContext::Expected("tagger <signature>".into()))
103 .parse_next(input)?;
104 *state = Message;
105 Token::Tagger(signature)
106 }
107 Message => {
108 let (message, pgp_signature) = terminated(decode::message, eof).parse_next(input)?;
109 debug_assert!(
110 input.is_empty(),
111 "we should have consumed all data - otherwise iter may go forever"
112 );
113 Token::Body { message, pgp_signature }
114 }
115 })
116 }
117}
118
119impl<'a> Iterator for TagRefIter<'a> {
120 type Item = Result<Token<'a>, crate::decode::Error>;
121
122 fn next(&mut self) -> Option<Self::Item> {
123 if self.data.is_empty() {
124 return None;
125 }
126 match Self::next_inner(self.data, &mut self.state) {
127 Ok((data, token)) => {
128 self.data = data;
129 Some(Ok(token))
130 }
131 Err(err) => {
132 self.data = &[];
133 Some(Err(err))
134 }
135 }
136 }
137}
138
139#[allow(missing_docs)]
141#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]
142pub enum Token<'a> {
143 Target {
144 id: ObjectId,
145 },
146 TargetKind(Kind),
147 Name(&'a BStr),
148 Tagger(Option<gix_actor::SignatureRef<'a>>),
149 Body {
150 message: &'a BStr,
151 pgp_signature: Option<&'a BStr>,
152 },
153}
154
155impl Token<'_> {
156 pub fn id(&self) -> Option<&oid> {
158 match self {
159 Token::Target { id } => Some(id.as_ref()),
160 _ => None,
161 }
162 }
163
164 pub fn into_id(self) -> Option<ObjectId> {
166 match self {
167 Token::Target { id } => Some(id),
168 _ => None,
169 }
170 }
171}