1use std::ops::Range;
2
3use bstr::{BStr, BString, ByteSlice};
4use winnow::prelude::*;
5
6use crate::parse::parse_signature;
7use crate::{Commit, CommitRef, TagRef};
8
9pub const SIGNATURE_FIELD_NAME: &str = "gpgsig";
11
12mod decode;
13pub mod message;
15
16#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]
20#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
21pub struct MessageRef<'a> {
22 #[cfg_attr(feature = "serde", serde(borrow))]
24 pub title: &'a BStr,
25 pub body: Option<&'a BStr>,
29}
30
31#[derive(PartialEq, Eq, Debug, Hash, Clone)]
36#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
37pub struct SignedData<'a> {
38 data: &'a [u8],
40 signature_range: Range<usize>,
42}
43
44impl SignedData<'_> {
45 pub fn to_bstring(&self) -> BString {
47 let mut buf = BString::from(&self.data[..self.signature_range.start]);
48 buf.extend_from_slice(&self.data[self.signature_range.end..]);
49 buf
50 }
51}
52
53impl From<SignedData<'_>> for BString {
54 fn from(value: SignedData<'_>) -> Self {
55 value.to_bstring()
56 }
57}
58
59pub mod ref_iter;
61
62mod write;
63
64impl<'a> CommitRef<'a> {
66 pub fn from_bytes(mut data: &'a [u8]) -> Result<CommitRef<'a>, crate::decode::Error> {
68 let input = &mut data;
69 match decode::commit.parse_next(input) {
70 Ok(tag) => Ok(tag),
71 Err(err) => Err(crate::decode::Error::with_err(err, input)),
72 }
73 }
74}
75
76impl<'a> CommitRef<'a> {
78 pub fn tree(&self) -> gix_hash::ObjectId {
80 gix_hash::ObjectId::from_hex(self.tree).expect("prior validation of tree hash during parsing")
81 }
82
83 pub fn parents(&self) -> impl Iterator<Item = gix_hash::ObjectId> + '_ {
85 self.parents
86 .iter()
87 .map(|hex_hash| gix_hash::ObjectId::from_hex(hex_hash).expect("prior validation of hashes during parsing"))
88 }
89
90 pub fn extra_headers(&self) -> ExtraHeaders<impl Iterator<Item = (&BStr, &BStr)>> {
92 ExtraHeaders::new(self.extra_headers.iter().map(|(k, v)| (*k, v.as_ref())))
93 }
94
95 pub fn author(&self) -> Result<gix_actor::SignatureRef<'a>, crate::decode::Error> {
99 parse_signature(self.author).map(|signature| signature.trim())
100 }
101
102 pub fn committer(&self) -> Result<gix_actor::SignatureRef<'a>, crate::decode::Error> {
106 parse_signature(self.committer).map(|signature| signature.trim())
107 }
108
109 pub fn message(&self) -> MessageRef<'a> {
111 MessageRef::from_bytes(self.message)
112 }
113
114 pub fn time(&self) -> Result<gix_date::Time, crate::decode::Error> {
116 parse_signature(self.committer).map(|signature| signature.time().unwrap_or_default())
117 }
118}
119
120impl CommitRef<'_> {
122 pub fn into_owned(self) -> Result<Commit, crate::decode::Error> {
124 self.try_into()
125 }
126
127 pub fn to_owned(self) -> Result<Commit, crate::decode::Error> {
129 self.try_into()
130 }
131}
132
133impl Commit {
134 pub fn extra_headers(&self) -> ExtraHeaders<impl Iterator<Item = (&BStr, &BStr)>> {
136 ExtraHeaders::new(self.extra_headers.iter().map(|(k, v)| (k.as_bstr(), v.as_bstr())))
137 }
138}
139
140pub struct ExtraHeaders<I> {
142 inner: I,
143}
144
145impl<'a, I> ExtraHeaders<I>
147where
148 I: Iterator<Item = (&'a BStr, &'a BStr)>,
149{
150 pub fn new(iter: I) -> Self {
152 ExtraHeaders { inner: iter }
153 }
154
155 pub fn find(mut self, name: &str) -> Option<&'a BStr> {
157 self.inner
158 .find_map(move |(k, v)| if k == name.as_bytes().as_bstr() { Some(v) } else { None })
159 }
160
161 pub fn find_pos(self, name: &str) -> Option<usize> {
163 self.inner
164 .enumerate()
165 .find_map(|(pos, (field, _value))| (field == name).then_some(pos))
166 }
167
168 pub fn find_all(self, name: &'a str) -> impl Iterator<Item = &'a BStr> {
170 self.inner
171 .filter_map(move |(k, v)| if k == name.as_bytes().as_bstr() { Some(v) } else { None })
172 }
173
174 pub fn mergetags(self) -> impl Iterator<Item = Result<TagRef<'a>, crate::decode::Error>> {
179 self.find_all("mergetag").map(|b| TagRef::from_bytes(b))
180 }
181
182 pub fn pgp_signature(self) -> Option<&'a BStr> {
184 self.find(SIGNATURE_FIELD_NAME)
185 }
186}