git_object/tag/
ref_iter.rs1use bstr::BStr;
2use git_hash::{oid, ObjectId};
3use nom::{
4 bytes::complete::take_while1,
5 character::is_alphabetic,
6 combinator::{all_consuming, opt},
7 error::{context, ParseError},
8};
9
10use crate::{bstr::ByteSlice, parse, parse::NL, tag::decode, Kind, TagRefIter};
11
12#[derive(Copy, Clone)]
13pub(crate) enum State {
14 Target,
15 TargetKind,
16 Name,
17 Tagger,
18 Message,
19}
20
21impl Default for State {
22 fn default() -> Self {
23 State::Target
24 }
25}
26
27impl<'a> TagRefIter<'a> {
28 pub fn from_bytes(data: &'a [u8]) -> TagRefIter<'a> {
30 TagRefIter {
31 data,
32 state: State::default(),
33 }
34 }
35
36 pub fn target_id(mut self) -> Result<ObjectId, crate::decode::Error> {
44 let token = self.next().ok_or_else(missing_field)??;
45 Token::into_id(token).ok_or_else(missing_field)
46 }
47
48 pub fn tagger(mut self) -> Result<Option<git_actor::SignatureRef<'a>>, crate::decode::Error> {
51 self.find_map(|t| match t {
52 Ok(Token::Tagger(signature)) => Some(Ok(signature)),
53 Err(err) => Some(Err(err)),
54 _ => None,
55 })
56 .ok_or_else(missing_field)?
57 }
58}
59
60fn missing_field() -> crate::decode::Error {
61 crate::decode::empty_error()
62}
63
64impl<'a> TagRefIter<'a> {
65 fn next_inner(i: &'a [u8], state: &mut State) -> Result<(&'a [u8], Token<'a>), crate::decode::Error> {
66 use State::*;
67 Ok(match state {
68 Target => {
69 let (i, target) = context("object <40 lowercase hex char>", |i| {
70 parse::header_field(i, b"object", parse::hex_hash)
71 })(i)?;
72 *state = TargetKind;
73 (
74 i,
75 Token::Target {
76 id: ObjectId::from_hex(target).expect("parsing validation"),
77 },
78 )
79 }
80 TargetKind => {
81 let (i, kind) = context("type <object kind>", |i| {
82 parse::header_field(i, b"type", take_while1(is_alphabetic))
83 })(i)?;
84 let kind = Kind::from_bytes(kind).map_err(|_| {
85 #[allow(clippy::let_unit_value)]
86 {
87 let err = crate::decode::ParseError::from_error_kind(i, nom::error::ErrorKind::MapRes);
88 nom::Err::Error(err)
89 }
90 })?;
91 *state = Name;
92 (i, Token::TargetKind(kind))
93 }
94 Name => {
95 let (i, tag_version) = context("tag <version>", |i| {
96 parse::header_field(i, b"tag", take_while1(|b| b != NL[0]))
97 })(i)?;
98 *state = Tagger;
99 (i, Token::Name(tag_version.as_bstr()))
100 }
101 Tagger => {
102 let (i, signature) = context(
103 "tagger <signature>",
104 opt(|i| parse::header_field(i, b"tagger", parse::signature)),
105 )(i)?;
106 *state = Message;
107 (i, Token::Tagger(signature))
108 }
109 Message => {
110 let (i, (message, pgp_signature)) = all_consuming(decode::message)(i)?;
111 debug_assert!(
112 i.is_empty(),
113 "we should have consumed all data - otherwise iter may go forever"
114 );
115 return Ok((i, Token::Body { message, pgp_signature }));
116 }
117 })
118 }
119}
120
121impl<'a> Iterator for TagRefIter<'a> {
122 type Item = Result<Token<'a>, crate::decode::Error>;
123
124 fn next(&mut self) -> Option<Self::Item> {
125 if self.data.is_empty() {
126 return None;
127 }
128 match Self::next_inner(self.data, &mut self.state) {
129 Ok((data, token)) => {
130 self.data = data;
131 Some(Ok(token))
132 }
133 Err(err) => {
134 self.data = &[];
135 Some(Err(err))
136 }
137 }
138 }
139}
140
141#[allow(missing_docs)]
143#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]
144pub enum Token<'a> {
145 Target {
146 id: ObjectId,
147 },
148 TargetKind(Kind),
149 Name(&'a BStr),
150 Tagger(Option<git_actor::SignatureRef<'a>>),
151 Body {
152 message: &'a BStr,
153 pgp_signature: Option<&'a BStr>,
154 },
155}
156
157impl<'a> Token<'a> {
158 pub fn id(&self) -> Option<&oid> {
160 match self {
161 Token::Target { id } => Some(id.as_ref()),
162 _ => None,
163 }
164 }
165
166 pub fn into_id(self) -> Option<ObjectId> {
168 match self {
169 Token::Target { id } => Some(id),
170 _ => None,
171 }
172 }
173}