mit_commit/trailers.rs
1use std::{convert::TryFrom, slice::Iter};
2
3use crate::{fragment::Fragment, trailer::Trailer};
4
5/// A Collection of `Trailer`
6#[derive(Debug, PartialEq, Eq, Clone, Default)]
7pub struct Trailers<'a> {
8 trailers: Vec<Trailer<'a>>,
9 iterator_index: usize,
10}
11
12impl Trailers<'_> {
13 /// Iterate over the [`Trailers`]
14 ///
15 /// # Examples
16 ///
17 /// ```
18 /// use mit_commit::{Trailer, Trailers};
19 /// let trailers = Trailers::from(vec![
20 /// Trailer::new(
21 /// "Co-authored-by".into(),
22 /// "Billie Thompson <billie@example.com>".into(),
23 /// ),
24 /// Trailer::new(
25 /// "Co-authored-by".into(),
26 /// "Someone Else <someone@example.com>".into(),
27 /// ),
28 /// Trailer::new("Relates-to".into(), "#124".into()),
29 /// ]);
30 /// let mut iterator = trailers.iter();
31 ///
32 /// assert_eq!(
33 /// iterator.next(),
34 /// Some(&Trailer::new(
35 /// "Co-authored-by".into(),
36 /// "Billie Thompson <billie@example.com>".into()
37 /// ))
38 /// );
39 /// assert_eq!(
40 /// iterator.next(),
41 /// Some(&Trailer::new(
42 /// "Co-authored-by".into(),
43 /// "Someone Else <someone@example.com>".into()
44 /// ))
45 /// );
46 /// assert_eq!(
47 /// iterator.next(),
48 /// Some(&Trailer::new("Relates-to".into(), "#124".into()))
49 /// );
50 /// assert_eq!(iterator.next(), None);
51 /// ```
52 pub fn iter(&self) -> Iter<'_, Trailer<'_>> {
53 self.trailers.iter()
54 }
55
56 /// How many [`Trailers`] are there
57 ///
58 /// # Examples
59 ///
60 /// ```
61 /// use mit_commit::{Trailer, Trailers};
62 /// let trailers = Trailers::from(vec![
63 /// Trailer::new(
64 /// "Co-authored-by".into(),
65 /// "Billie Thompson <billie@example.com>".into(),
66 /// ),
67 /// Trailer::new(
68 /// "Co-authored-by".into(),
69 /// "Someone Else <someone@example.com>".into(),
70 /// ),
71 /// ]);
72 ///
73 /// assert_eq!(trailers.len(), 2)
74 /// ```
75 #[must_use]
76 pub fn len(&self) -> usize {
77 self.trailers.len()
78 }
79
80 /// Are there no [`Trailers`]
81 ///
82 /// # Examples
83 ///
84 /// ```
85 /// use mit_commit::{Trailer, Trailers};
86 /// assert_eq!(
87 /// Trailers::from(vec![
88 /// Trailer::new(
89 /// "Co-authored-by".into(),
90 /// "Billie Thompson <billie@example.com>".into()
91 /// ),
92 /// Trailer::new(
93 /// "Co-authored-by".into(),
94 /// "Someone Else <someone@example.com>".into()
95 /// ),
96 /// ])
97 /// .is_empty(),
98 /// false
99 /// );
100 ///
101 /// let trailers: Vec<Trailer> = Vec::new();
102 /// assert_eq!(Trailers::from(trailers).is_empty(), true)
103 /// ```
104 #[must_use]
105 pub fn is_empty(&self) -> bool {
106 self.trailers.is_empty()
107 }
108}
109
110impl<'a> IntoIterator for Trailers<'a> {
111 type IntoIter = std::vec::IntoIter<Trailer<'a>>;
112 type Item = Trailer<'a>;
113
114 /// Iterate over the [`Trailers`]
115 ///
116 /// # Examples
117 ///
118 /// ```
119 /// use mit_commit::{Trailer, Trailers};
120 /// let trailers = Trailers::from(vec![
121 /// Trailer::new(
122 /// "Co-authored-by".into(),
123 /// "Billie Thompson <billie@example.com>".into(),
124 /// ),
125 /// Trailer::new(
126 /// "Co-authored-by".into(),
127 /// "Someone Else <someone@example.com>".into(),
128 /// ),
129 /// Trailer::new("Relates-to".into(), "#124".into()),
130 /// ]);
131 /// let mut iterator = trailers.into_iter();
132 ///
133 /// assert_eq!(
134 /// iterator.next(),
135 /// Some(Trailer::new(
136 /// "Co-authored-by".into(),
137 /// "Billie Thompson <billie@example.com>".into()
138 /// ))
139 /// );
140 /// assert_eq!(
141 /// iterator.next(),
142 /// Some(Trailer::new(
143 /// "Co-authored-by".into(),
144 /// "Someone Else <someone@example.com>".into()
145 /// ))
146 /// );
147 /// assert_eq!(
148 /// iterator.next(),
149 /// Some(Trailer::new("Relates-to".into(), "#124".into()))
150 /// );
151 /// assert_eq!(iterator.next(), None);
152 /// ```
153 fn into_iter(self) -> Self::IntoIter {
154 self.trailers.into_iter()
155 }
156}
157impl<'a> IntoIterator for &'a Trailers<'a> {
158 type IntoIter = Iter<'a, Trailer<'a>>;
159 type Item = &'a Trailer<'a>;
160
161 /// Iterate over the [`Trailers`]
162 ///
163 /// # Examples
164 ///
165 /// ```
166 /// use std::borrow::Borrow;
167 ///
168 /// use mit_commit::{Trailer, Trailers};
169 /// let trailers = Trailers::from(vec![
170 /// Trailer::new(
171 /// "Co-authored-by".into(),
172 /// "Billie Thompson <billie@example.com>".into(),
173 /// ),
174 /// Trailer::new(
175 /// "Co-authored-by".into(),
176 /// "Someone Else <someone@example.com>".into(),
177 /// ),
178 /// Trailer::new("Relates-to".into(), "#124".into()),
179 /// ]);
180 /// let trailer_ref = trailers.borrow();
181 /// let mut iterator = trailer_ref.into_iter();
182 ///
183 /// assert_eq!(
184 /// iterator.next(),
185 /// Some(&Trailer::new(
186 /// "Co-authored-by".into(),
187 /// "Billie Thompson <billie@example.com>".into()
188 /// ))
189 /// );
190 /// assert_eq!(
191 /// iterator.next(),
192 /// Some(&Trailer::new(
193 /// "Co-authored-by".into(),
194 /// "Someone Else <someone@example.com>".into()
195 /// ))
196 /// );
197 /// assert_eq!(
198 /// iterator.next(),
199 /// Some(&Trailer::new("Relates-to".into(), "#124".into()))
200 /// );
201 /// assert_eq!(iterator.next(), None);
202 /// ```
203 fn into_iter(self) -> Self::IntoIter {
204 self.trailers.iter()
205 }
206}
207
208impl<'a> From<Vec<Trailer<'a>>> for Trailers<'a> {
209 fn from(trailers: Vec<Trailer<'a>>) -> Self {
210 Self {
211 trailers,
212 iterator_index: 0,
213 }
214 }
215}
216
217impl<'a> From<Trailers<'a>> for String {
218 fn from(trailers: Trailers<'a>) -> Self {
219 trailers
220 .trailers
221 .into_iter()
222 .map(Self::from)
223 .collect::<Vec<_>>()
224 .join("\n")
225 }
226}
227
228impl<'a> From<Vec<Fragment<'a>>> for Trailers<'a> {
229 fn from(ast: Vec<Fragment<'a>>) -> Self {
230 ast.into_iter()
231 .skip(1)
232 .filter_map(|values| {
233 if let Fragment::Body(body) = values {
234 Some(body)
235 } else {
236 None
237 }
238 })
239 .rev()
240 .filter_map(|body| {
241 if body.is_empty() {
242 None
243 } else {
244 Some(Trailer::try_from(body))
245 }
246 })
247 .take_while(Result::is_ok)
248 .flatten()
249 .collect::<Vec<Trailer<'_>>>()
250 .into_iter()
251 .rev()
252 .collect::<Vec<Trailer<'_>>>()
253 .into()
254 }
255}