1pub mod headers;
2pub mod trailers;
3
4mod parse;
5pub use parse::ParseError;
6
7use core::fmt;
8use std::str::{self, FromStr};
9
10use headers::{Headers, Signature};
11use trailers::{OwnedTrailer, Trailer};
12
13use crate::author::Author;
14
15#[derive(Clone, Debug, PartialEq, Eq, Hash)]
18pub struct CommitData<Tree, Parent> {
19 tree: Tree,
20 parents: Vec<Parent>,
21 author: Author,
22 committer: Author,
23 headers: Headers,
24 message: String,
25 trailers: Vec<OwnedTrailer>,
26}
27
28impl<Tree, Parent> CommitData<Tree, Parent> {
29 pub fn new<P, I, T>(
30 tree: Tree,
31 parents: P,
32 author: Author,
33 committer: Author,
34 headers: Headers,
35 message: String,
36 trailers: I,
37 ) -> Self
38 where
39 P: IntoIterator<Item = Parent>,
40 I: IntoIterator<Item = T>,
41 OwnedTrailer: From<T>,
42 {
43 let trailers = trailers.into_iter().map(OwnedTrailer::from).collect();
44 let parents = parents.into_iter().collect();
45 Self {
46 tree,
47 parents,
48 author,
49 committer,
50 headers,
51 message,
52 trailers,
53 }
54 }
55
56 pub fn tree(&self) -> &Tree {
58 &self.tree
59 }
60
61 pub fn parents(&self) -> impl Iterator<Item = Parent> + '_
63 where
64 Parent: Clone,
65 {
66 self.parents.iter().cloned()
67 }
68
69 pub fn author(&self) -> &Author {
71 &self.author
72 }
73
74 pub fn committer(&self) -> &Author {
77 &self.committer
78 }
79
80 pub fn message(&self) -> &str {
82 &self.message
83 }
84
85 pub fn signatures(&self) -> impl Iterator<Item = Signature<'_>> + '_ {
88 self.headers.signatures()
89 }
90
91 pub fn strip_signatures(mut self) -> Self {
92 self.headers.strip_signatures();
93 self
94 }
95
96 pub fn headers(&self) -> impl Iterator<Item = (&str, &str)> {
100 self.headers.iter()
101 }
102
103 pub fn values<'a>(&'a self, name: &'a str) -> impl Iterator<Item = &'a str> + 'a {
105 self.headers.values(name)
106 }
107
108 pub fn push_header(&mut self, name: &str, value: &str) {
110 self.headers.push(name, value.trim());
111 }
112
113 pub fn trailers(&self) -> impl Iterator<Item = &OwnedTrailer> {
114 self.trailers.iter()
115 }
116
117 pub fn map_tree<U, E, F>(self, f: F) -> Result<CommitData<U, Parent>, E>
123 where
124 F: FnOnce(Tree) -> Result<U, E>,
125 {
126 Ok(CommitData {
127 tree: f(self.tree)?,
128 parents: self.parents,
129 author: self.author,
130 committer: self.committer,
131 headers: self.headers,
132 message: self.message,
133 trailers: self.trailers,
134 })
135 }
136
137 pub fn map_parents<U, E, F>(self, f: F) -> Result<CommitData<Tree, U>, E>
144 where
145 F: FnMut(Parent) -> Result<U, E>,
146 {
147 Ok(CommitData {
148 tree: self.tree,
149 parents: self
150 .parents
151 .into_iter()
152 .map(f)
153 .collect::<Result<Vec<_>, _>>()?,
154 author: self.author,
155 committer: self.committer,
156 headers: self.headers,
157 message: self.message,
158 trailers: self.trailers,
159 })
160 }
161}
162
163impl<Tree, Parent> CommitData<Tree, Parent>
164where
165 Tree: str::FromStr,
166 Parent: str::FromStr,
167 Tree::Err: std::error::Error + Send + Sync + 'static,
168 Parent::Err: std::error::Error + Send + Sync + 'static,
169{
170 pub fn from_bytes(bytes: &[u8]) -> Result<Self, ParseError> {
182 let s = str::from_utf8(bytes).map_err(ParseError::Utf8)?;
183 parse::parse(s)
184 }
185}
186
187impl<Tree, Parent> FromStr for CommitData<Tree, Parent>
188where
189 Tree: str::FromStr,
190 Parent: str::FromStr,
191 Tree::Err: std::error::Error + Send + Sync + 'static,
192 Parent::Err: std::error::Error + Send + Sync + 'static,
193{
194 type Err = ParseError;
195
196 fn from_str(s: &str) -> Result<Self, Self::Err> {
197 parse::parse(s)
198 }
199}
200
201impl<Tree: fmt::Display, Parent: fmt::Display> fmt::Display for CommitData<Tree, Parent> {
202 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
203 writeln!(f, "tree {}", self.tree)?;
204 for parent in self.parents.iter() {
205 writeln!(f, "parent {parent}")?;
206 }
207 writeln!(f, "author {}", self.author)?;
208 writeln!(f, "committer {}", self.committer)?;
209
210 for (name, value) in self.headers.iter() {
211 writeln!(f, "{name} {}", value.replace('\n', "\n "))?;
212 }
213 writeln!(f)?;
214 write!(f, "{}", self.message.trim())?;
215 writeln!(f)?;
216
217 if !self.trailers.is_empty() {
218 writeln!(f)?;
219 }
220 for trailer in self.trailers.iter() {
221 writeln!(f, "{}", Trailer::from(trailer).display(": "))?;
222 }
223 Ok(())
224 }
225}