]> git.proxmox.com Git - rustc.git/blame - vendor/gix-object/src/tag/ref_iter.rs
New upstream version 1.76.0+dfsg1
[rustc.git] / vendor / gix-object / src / tag / ref_iter.rs
CommitLineData
0a29b90c
FG
1use bstr::BStr;
2use gix_hash::{oid, ObjectId};
781aab86
FG
3use winnow::{
4 combinator::{eof, opt, terminated},
5 error::{ParserError, StrContext},
6 prelude::*,
7 stream::AsChar,
8 token::take_while,
0a29b90c
FG
9};
10
11use crate::{bstr::ByteSlice, parse, parse::NL, tag::decode, Kind, TagRefIter};
12
49aad941 13#[derive(Default, Copy, Clone)]
0a29b90c 14pub(crate) enum State {
49aad941 15 #[default]
0a29b90c
FG
16 Target,
17 TargetKind,
18 Name,
19 Tagger,
20 Message,
21}
22
0a29b90c
FG
23impl<'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
56fn missing_field() -> crate::decode::Error {
57 crate::decode::empty_error()
58}
59
60impl<'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
120impl<'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)]
143pub 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
156impl<'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}