1use std::ops::Range;
2
3use bstr::{BStr, BString, ByteSlice};
4use winnow::prelude::*;
5
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]) -> Result<CommitRef<'a>, crate::decode::Error> {
67 let input = &mut data;
68 match decode::commit.parse_next(input) {
69 Ok(tag) => Ok(tag),
70 Err(err) => Err(crate::decode::Error::with_err(err, input)),
71 }
72 }
73}
74
75impl<'a> CommitRef<'a> {
77 pub fn tree(&self) -> gix_hash::ObjectId {
79 gix_hash::ObjectId::from_hex(self.tree).expect("prior validation of tree hash during parsing")
80 }
81
82 pub fn parents(&self) -> impl Iterator<Item = gix_hash::ObjectId> + '_ {
84 self.parents
85 .iter()
86 .map(|hex_hash| gix_hash::ObjectId::from_hex(hex_hash).expect("prior validation of hashes during parsing"))
87 }
88
89 pub fn extra_headers(&self) -> ExtraHeaders<impl Iterator<Item = (&BStr, &BStr)>> {
91 ExtraHeaders::new(self.extra_headers.iter().map(|(k, v)| (*k, v.as_ref())))
92 }
93
94 pub fn author(&self) -> gix_actor::SignatureRef<'a> {
98 self.author.trim()
99 }
100
101 pub fn committer(&self) -> gix_actor::SignatureRef<'a> {
105 self.committer.trim()
106 }
107
108 pub fn message(&self) -> MessageRef<'a> {
110 MessageRef::from_bytes(self.message)
111 }
112
113 pub fn time(&self) -> gix_date::Time {
115 self.committer.time
116 }
117}
118
119impl CommitRef<'_> {
121 pub fn into_owned(self) -> Commit {
123 self.into()
124 }
125
126 pub fn to_owned(self) -> Commit {
128 self.clone().into()
129 }
130}
131
132impl Commit {
133 pub fn extra_headers(&self) -> ExtraHeaders<impl Iterator<Item = (&BStr, &BStr)>> {
135 ExtraHeaders::new(self.extra_headers.iter().map(|(k, v)| (k.as_bstr(), v.as_bstr())))
136 }
137}
138
139pub struct ExtraHeaders<I> {
141 inner: I,
142}
143
144impl<'a, I> ExtraHeaders<I>
146where
147 I: Iterator<Item = (&'a BStr, &'a BStr)>,
148{
149 pub fn new(iter: I) -> Self {
151 ExtraHeaders { inner: iter }
152 }
153
154 pub fn find(mut self, name: &str) -> Option<&'a BStr> {
156 self.inner
157 .find_map(move |(k, v)| if k == name.as_bytes().as_bstr() { Some(v) } else { None })
158 }
159
160 pub fn find_pos(self, name: &str) -> Option<usize> {
162 self.inner
163 .enumerate()
164 .find_map(|(pos, (field, _value))| (field == name).then_some(pos))
165 }
166
167 pub fn find_all(self, name: &'a str) -> impl Iterator<Item = &'a BStr> {
169 self.inner
170 .filter_map(move |(k, v)| if k == name.as_bytes().as_bstr() { Some(v) } else { None })
171 }
172
173 pub fn mergetags(self) -> impl Iterator<Item = Result<TagRef<'a>, crate::decode::Error>> {
178 self.find_all("mergetag").map(|b| TagRef::from_bytes(b))
179 }
180
181 pub fn pgp_signature(self) -> Option<&'a BStr> {
183 self.find(SIGNATURE_FIELD_NAME)
184 }
185}