Skip to main content

gix_object/tag/
ref_iter.rs

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