1use core::ops::Range;
2use std::ffi::CStr;
3use std::ffi::CString;
4use std::iter::FusedIterator;
5use std::ptr;
6
7use libc::{c_char, c_int};
8
9use crate::util::Binding;
10use crate::{raw, Buf, Error, IntoCString};
11
12pub fn message_prettify<T: IntoCString>(
16 message: T,
17 comment_char: Option<u8>,
18) -> Result<String, Error> {
19 _message_prettify(message.into_c_string()?, comment_char)
20}
21
22fn _message_prettify(message: CString, comment_char: Option<u8>) -> Result<String, Error> {
23 let ret = Buf::new();
24 unsafe {
25 try_call!(raw::git_message_prettify(
26 ret.raw(),
27 message,
28 comment_char.is_some() as c_int,
29 comment_char.unwrap_or(0) as c_char
30 ));
31 }
32 Ok(ret.as_str().unwrap().to_string())
33}
34
35pub const DEFAULT_COMMENT_CHAR: Option<u8> = Some(b'#');
37
38pub fn message_trailers_strs(message: &str) -> Result<MessageTrailersStrs, Error> {
42 _message_trailers(message.into_c_string()?).map(|res| MessageTrailersStrs(res))
43}
44
45pub fn message_trailers_bytes<S: IntoCString>(message: S) -> Result<MessageTrailersBytes, Error> {
51 _message_trailers(message.into_c_string()?).map(|res| MessageTrailersBytes(res))
52}
53
54fn _message_trailers(message: CString) -> Result<MessageTrailers, Error> {
55 let ret = MessageTrailers::new();
56 unsafe {
57 try_call!(raw::git_message_trailers(ret.raw(), message));
58 }
59 Ok(ret)
60}
61
62pub struct MessageTrailersStrs(MessageTrailers);
66
67impl MessageTrailersStrs {
68 pub fn iter(&self) -> MessageTrailersStrsIterator<'_> {
70 MessageTrailersStrsIterator(self.0.iter())
71 }
72 pub fn len(&self) -> usize {
74 self.0.len()
75 }
76 pub fn to_bytes(self) -> MessageTrailersBytes {
78 MessageTrailersBytes(self.0)
79 }
80}
81
82pub struct MessageTrailersBytes(MessageTrailers);
86
87impl MessageTrailersBytes {
88 pub fn iter(&self) -> MessageTrailersBytesIterator<'_> {
90 MessageTrailersBytesIterator(self.0.iter())
91 }
92 pub fn len(&self) -> usize {
94 self.0.len()
95 }
96}
97
98struct MessageTrailers {
99 raw: raw::git_message_trailer_array,
100}
101
102impl MessageTrailers {
103 fn new() -> MessageTrailers {
104 crate::init();
105 unsafe {
106 Binding::from_raw(&mut raw::git_message_trailer_array {
107 trailers: ptr::null_mut(),
108 count: 0,
109 _trailer_block: ptr::null_mut(),
110 } as *mut _)
111 }
112 }
113 fn iter(&self) -> MessageTrailersIterator<'_> {
114 MessageTrailersIterator {
115 trailers: self,
116 range: Range {
117 start: 0,
118 end: self.raw.count,
119 },
120 }
121 }
122 fn len(&self) -> usize {
123 self.raw.count
124 }
125}
126
127impl Drop for MessageTrailers {
128 fn drop(&mut self) {
129 unsafe {
130 raw::git_message_trailer_array_free(&mut self.raw);
131 }
132 }
133}
134
135impl Binding for MessageTrailers {
136 type Raw = *mut raw::git_message_trailer_array;
137 unsafe fn from_raw(raw: *mut raw::git_message_trailer_array) -> MessageTrailers {
138 MessageTrailers { raw: *raw }
139 }
140 fn raw(&self) -> *mut raw::git_message_trailer_array {
141 &self.raw as *const _ as *mut _
142 }
143}
144
145struct MessageTrailersIterator<'a> {
146 trailers: &'a MessageTrailers,
147 range: Range<usize>,
148}
149
150fn to_raw_tuple(trailers: &MessageTrailers, index: usize) -> (*const c_char, *const c_char) {
151 unsafe {
152 let addr = trailers.raw.trailers.wrapping_add(index);
153 ((*addr).key, (*addr).value)
154 }
155}
156
157pub struct MessageTrailersStrsIterator<'a>(MessageTrailersIterator<'a>);
159
160impl<'pair> Iterator for MessageTrailersStrsIterator<'pair> {
161 type Item = (&'pair str, &'pair str);
162
163 fn next(&mut self) -> Option<Self::Item> {
164 self.0
165 .range
166 .next()
167 .map(|index| to_str_tuple(&self.0.trailers, index))
168 }
169
170 fn size_hint(&self) -> (usize, Option<usize>) {
171 self.0.range.size_hint()
172 }
173}
174
175impl FusedIterator for MessageTrailersStrsIterator<'_> {}
176
177impl ExactSizeIterator for MessageTrailersStrsIterator<'_> {
178 fn len(&self) -> usize {
179 self.0.range.len()
180 }
181}
182
183impl DoubleEndedIterator for MessageTrailersStrsIterator<'_> {
184 fn next_back(&mut self) -> Option<Self::Item> {
185 self.0
186 .range
187 .next_back()
188 .map(|index| to_str_tuple(&self.0.trailers, index))
189 }
190}
191
192fn to_str_tuple(trailers: &MessageTrailers, index: usize) -> (&str, &str) {
193 unsafe {
194 let (rkey, rvalue) = to_raw_tuple(&trailers, index);
195 let key = CStr::from_ptr(rkey).to_str().unwrap();
196 let value = CStr::from_ptr(rvalue).to_str().unwrap();
197 (key, value)
198 }
199}
200
201pub struct MessageTrailersBytesIterator<'a>(MessageTrailersIterator<'a>);
203
204impl<'pair> Iterator for MessageTrailersBytesIterator<'pair> {
205 type Item = (&'pair [u8], &'pair [u8]);
206
207 fn next(&mut self) -> Option<Self::Item> {
208 self.0
209 .range
210 .next()
211 .map(|index| to_bytes_tuple(&self.0.trailers, index))
212 }
213
214 fn size_hint(&self) -> (usize, Option<usize>) {
215 self.0.range.size_hint()
216 }
217}
218
219impl FusedIterator for MessageTrailersBytesIterator<'_> {}
220
221impl ExactSizeIterator for MessageTrailersBytesIterator<'_> {
222 fn len(&self) -> usize {
223 self.0.range.len()
224 }
225}
226
227impl DoubleEndedIterator for MessageTrailersBytesIterator<'_> {
228 fn next_back(&mut self) -> Option<Self::Item> {
229 self.0
230 .range
231 .next_back()
232 .map(|index| to_bytes_tuple(&self.0.trailers, index))
233 }
234}
235
236fn to_bytes_tuple(trailers: &MessageTrailers, index: usize) -> (&[u8], &[u8]) {
237 unsafe {
238 let (rkey, rvalue) = to_raw_tuple(&trailers, index);
239 let key = CStr::from_ptr(rkey).to_bytes();
240 let value = CStr::from_ptr(rvalue).to_bytes();
241 (key, value)
242 }
243}
244
245#[cfg(test)]
246mod tests {
247
248 #[test]
249 fn prettify() {
250 use crate::{message_prettify, DEFAULT_COMMENT_CHAR};
251
252 assert_eq!(message_prettify("1\n\n\n2", None).unwrap(), "1\n\n2\n");
256 assert_eq!(
257 message_prettify("1\n\n\n2\n\n\n3", None).unwrap(),
258 "1\n\n2\n\n3\n"
259 );
260 assert_eq!(
261 message_prettify("1\n# comment\n# more", None).unwrap(),
262 "1\n# comment\n# more\n"
263 );
264 assert_eq!(
265 message_prettify("1\n# comment\n# more", DEFAULT_COMMENT_CHAR).unwrap(),
266 "1\n"
267 );
268 assert_eq!(
269 message_prettify("1\n; comment\n; more", Some(';' as u8)).unwrap(),
270 "1\n"
271 );
272 }
273
274 #[test]
275 fn trailers() {
276 use crate::{message_trailers_bytes, message_trailers_strs, MessageTrailersStrs};
277 use std::collections::HashMap;
278
279 let message1 = "
281WHAT ARE WE HERE FOR
282
283What are we here for?
284
285Just to be eaten?
286";
287 let expected: HashMap<&str, &str> = HashMap::new();
288 assert_eq!(expected, to_map(&message_trailers_strs(message1).unwrap()));
289
290 let message2 = "
292Attention all
293
294We are out of tomatoes.
295
296Spoken-by: Major Turnips
297Transcribed-by: Seargant Persimmons
298Signed-off-by: Colonel Kale
299";
300 let expected: HashMap<&str, &str> = vec![
301 ("Spoken-by", "Major Turnips"),
302 ("Transcribed-by", "Seargant Persimmons"),
303 ("Signed-off-by", "Colonel Kale"),
304 ]
305 .into_iter()
306 .collect();
307 assert_eq!(expected, to_map(&message_trailers_strs(message2).unwrap()));
308
309 let message3 = "
311The fate of Seargant Green-Peppers
312
313Seargant Green-Peppers was killed by Caterpillar Battalion 44.
314
315Signed-off-by: Colonel Kale
316---
317I never liked that guy, anyway.
318
319Opined-by: Corporal Garlic
320";
321 let expected: HashMap<&str, &str> = vec![("Signed-off-by", "Colonel Kale")]
322 .into_iter()
323 .collect();
324 assert_eq!(expected, to_map(&message_trailers_strs(message3).unwrap()));
325
326 let message4 = b"
329Be honest guys
330
331Am I a malformed brussels sprout?
332
333Signed-off-by: Lieutenant \xe2\x28\xa1prout
334";
335
336 let trailer = message_trailers_bytes(&message4[..]).unwrap();
337 let expected = (&b"Signed-off-by"[..], &b"Lieutenant \xe2\x28\xa1prout"[..]);
338 let actual = trailer.iter().next().unwrap();
339 assert_eq!(expected, actual);
340
341 fn to_map(trailers: &MessageTrailersStrs) -> HashMap<&str, &str> {
342 let mut map = HashMap::with_capacity(trailers.len());
343 for (key, value) in trailers.iter() {
344 map.insert(key, value);
345 }
346 map
347 }
348 }
349}