1use std::ops::Range;
2
3use bstr::{BStr, BString, ByteSlice};
4
5use crate::parse::parse_signature;
6use crate::{Commit, CommitRef, TagRef};
7
8pub const SIGNATURE_FIELD_NAME: &str = "gpgsig";
10
11mod decode;
12pub mod message;
14
15#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20pub struct MessageRef<'a> {
21 #[cfg_attr(feature = "serde", serde(borrow))]
23 pub title: &'a BStr,
24 pub body: Option<&'a BStr>,
28}
29
30#[derive(PartialEq, Eq, Debug, Hash, Clone)]
35#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
36pub struct SignedData<'a> {
37 data: &'a [u8],
39 signature_range: Range<usize>,
41}
42
43impl SignedData<'_> {
44 pub fn to_bstring(&self) -> BString {
46 let mut buf = BString::from(&self.data[..self.signature_range.start]);
47 buf.extend_from_slice(&self.data[self.signature_range.end..]);
48 buf
49 }
50}
51
52impl From<SignedData<'_>> for BString {
53 fn from(value: SignedData<'_>) -> Self {
54 value.to_bstring()
55 }
56}
57
58pub mod ref_iter;
60
61mod write;
62
63impl<'a> CommitRef<'a> {
65 pub fn from_bytes(mut data: &'a [u8], hash_kind: gix_hash::Kind) -> Result<CommitRef<'a>, crate::decode::Error> {
68 let input = &mut data;
69 match decode::commit(input, hash_kind) {
70 Ok(tag) => Ok(tag),
71 Err(err) => Err(err),
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(
93 self.extra_headers.iter().map(|(k, v)| (*k, v.as_ref())),
94 self.tree().kind(),
95 )
96 }
97
98 pub fn author(&self) -> Result<gix_actor::SignatureRef<'a>, crate::decode::Error> {
102 parse_signature(self.author).map(|signature| signature.trim())
103 }
104
105 pub fn committer(&self) -> Result<gix_actor::SignatureRef<'a>, crate::decode::Error> {
109 parse_signature(self.committer).map(|signature| signature.trim())
110 }
111
112 pub fn message(&self) -> MessageRef<'a> {
114 MessageRef::from_bytes(self.message)
115 }
116
117 pub fn time(&self) -> Result<gix_date::Time, crate::decode::Error> {
119 parse_signature(self.committer).map(|signature| signature.time().unwrap_or_default())
120 }
121}
122
123impl CommitRef<'_> {
125 pub fn into_owned(self) -> Result<Commit, crate::decode::Error> {
127 self.try_into()
128 }
129
130 pub fn to_owned(self) -> Result<Commit, crate::decode::Error> {
132 self.try_into()
133 }
134}
135
136impl Commit {
137 pub fn extra_headers(&self) -> ExtraHeaders<impl Iterator<Item = (&BStr, &BStr)>> {
139 ExtraHeaders::new(
140 self.extra_headers.iter().map(|(k, v)| (k.as_bstr(), v.as_bstr())),
141 self.tree.kind(),
142 )
143 }
144}
145
146pub struct ExtraHeaders<I> {
148 inner: I,
149 hash_kind: gix_hash::Kind,
150}
151
152impl<'a, I> ExtraHeaders<I>
154where
155 I: Iterator<Item = (&'a BStr, &'a BStr)>,
156{
157 pub fn new(iter: I, hash_kind: gix_hash::Kind) -> Self {
159 ExtraHeaders { inner: iter, hash_kind }
160 }
161
162 pub fn find(mut self, name: &str) -> Option<&'a BStr> {
164 self.inner
165 .find_map(move |(k, v)| if k == name.as_bytes().as_bstr() { Some(v) } else { None })
166 }
167
168 pub fn find_pos(self, name: &str) -> Option<usize> {
170 self.inner
171 .enumerate()
172 .find_map(|(pos, (field, _value))| (field == name).then_some(pos))
173 }
174
175 pub fn find_all(self, name: &'a str) -> impl Iterator<Item = &'a BStr> {
177 self.inner
178 .filter_map(move |(k, v)| if k == name.as_bytes().as_bstr() { Some(v) } else { None })
179 }
180
181 pub fn mergetags(self) -> impl Iterator<Item = Result<TagRef<'a>, crate::decode::Error>> {
186 let hash_kind = self.hash_kind;
187 self.find_all("mergetag").map(move |b| TagRef::from_bytes(b, hash_kind))
188 }
189
190 pub fn pgp_signature(self) -> Option<&'a BStr> {
192 self.find(SIGNATURE_FIELD_NAME)
193 }
194}