1use std::ops::Range;
2
3use bstr::{BStr, BString, ByteSlice};
4use winnow::prelude::*;
5
6use crate::{Commit, CommitRef, TagRef};
7
8mod decode;
9pub mod message;
11
12#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17pub struct MessageRef<'a> {
18 #[cfg_attr(feature = "serde", serde(borrow))]
20 pub title: &'a BStr,
21 pub body: Option<&'a BStr>,
25}
26
27#[derive(PartialEq, Eq, Debug, Hash, Clone)]
32#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
33pub struct SignedData<'a> {
34 data: &'a [u8],
36 signature_range: Range<usize>,
38}
39
40impl SignedData<'_> {
41 pub fn to_bstring(&self) -> BString {
43 let mut buf = BString::from(&self.data[..self.signature_range.start]);
44 buf.extend_from_slice(&self.data[self.signature_range.end..]);
45 buf
46 }
47}
48
49impl From<SignedData<'_>> for BString {
50 fn from(value: SignedData<'_>) -> Self {
51 value.to_bstring()
52 }
53}
54
55pub mod ref_iter;
57
58mod write;
59
60impl<'a> CommitRef<'a> {
62 pub fn from_bytes(mut data: &'a [u8]) -> Result<CommitRef<'a>, crate::decode::Error> {
64 let input = &mut data;
65 match decode::commit.parse_next(input) {
66 Ok(tag) => Ok(tag),
67 Err(err) => Err(crate::decode::Error::with_err(err, input)),
68 }
69 }
70}
71
72impl<'a> CommitRef<'a> {
74 pub fn tree(&self) -> gix_hash::ObjectId {
76 gix_hash::ObjectId::from_hex(self.tree).expect("prior validation of tree hash during parsing")
77 }
78
79 pub fn parents(&self) -> impl Iterator<Item = gix_hash::ObjectId> + '_ {
81 self.parents
82 .iter()
83 .map(|hex_hash| gix_hash::ObjectId::from_hex(hex_hash).expect("prior validation of hashes during parsing"))
84 }
85
86 pub fn extra_headers(&self) -> crate::commit::ExtraHeaders<impl Iterator<Item = (&BStr, &BStr)>> {
88 ExtraHeaders::new(self.extra_headers.iter().map(|(k, v)| (*k, v.as_ref())))
89 }
90
91 pub fn author(&self) -> gix_actor::SignatureRef<'a> {
95 self.author.trim()
96 }
97
98 pub fn committer(&self) -> gix_actor::SignatureRef<'a> {
102 self.committer.trim()
103 }
104
105 pub fn message(&self) -> MessageRef<'a> {
107 MessageRef::from_bytes(self.message)
108 }
109
110 pub fn time(&self) -> gix_date::Time {
112 self.committer.time
113 }
114}
115
116impl Commit {
117 pub fn extra_headers(&self) -> ExtraHeaders<impl Iterator<Item = (&BStr, &BStr)>> {
119 ExtraHeaders::new(self.extra_headers.iter().map(|(k, v)| (k.as_bstr(), v.as_bstr())))
120 }
121}
122
123pub struct ExtraHeaders<I> {
125 inner: I,
126}
127
128impl<'a, I> ExtraHeaders<I>
130where
131 I: Iterator<Item = (&'a BStr, &'a BStr)>,
132{
133 pub fn new(iter: I) -> Self {
135 ExtraHeaders { inner: iter }
136 }
137 pub fn find(mut self, name: &str) -> Option<&'a BStr> {
139 self.inner
140 .find_map(move |(k, v)| if k == name.as_bytes().as_bstr() { Some(v) } else { None })
141 }
142 pub fn find_all(self, name: &'a str) -> impl Iterator<Item = &'a BStr> {
144 self.inner
145 .filter_map(move |(k, v)| if k == name.as_bytes().as_bstr() { Some(v) } else { None })
146 }
147 pub fn mergetags(self) -> impl Iterator<Item = Result<TagRef<'a>, crate::decode::Error>> {
152 self.find_all("mergetag").map(|b| TagRef::from_bytes(b))
153 }
154
155 pub fn pgp_signature(self) -> Option<&'a BStr> {
157 self.find("gpgsig")
158 }
159}