mit_commit/bodies.rs
1use std::{
2 convert::TryFrom,
3 fmt,
4 fmt::{Display, Formatter},
5 slice::Iter,
6 vec::IntoIter,
7};
8
9use crate::{body::Body, fragment::Fragment, trailer::Trailer};
10
11/// A collection of user input [`CommitMessage`] text
12///
13/// # Examples
14///
15/// ```
16/// use mit_commit::{Bodies, Body, Subject};
17///
18/// let bodies: Vec<Body> = Vec::default();
19/// assert_eq!(None, Bodies::from(bodies).first());
20///
21/// let bodies: Vec<Body> = vec![
22/// Body::from("First"),
23/// Body::from("Second"),
24/// Body::from("Third"),
25/// ];
26/// assert_eq!(Some(Body::from("First")), Bodies::from(bodies).first());
27/// ```
28#[derive(Debug, PartialEq, Eq, Clone, Default)]
29pub struct Bodies<'a> {
30 bodies: Vec<Body<'a>>,
31}
32
33impl<'a> Bodies<'a> {
34 /// Get the first [`Body`] in this list of [`Bodies`]
35 ///
36 /// # Examples
37 ///
38 /// ```
39 /// use mit_commit::{Bodies, Body, Subject};
40 ///
41 /// let bodies: Vec<Body> = Vec::default();
42 /// assert_eq!(None, Bodies::from(bodies).first());
43 ///
44 /// let bodies: Vec<Body> = vec![
45 /// Body::from("First"),
46 /// Body::from("Second"),
47 /// Body::from("Third"),
48 /// ];
49 /// assert_eq!(Some(Body::from("First")), Bodies::from(bodies).first());
50 /// ```
51 #[must_use]
52 pub fn first(&self) -> Option<Body<'_>> {
53 self.bodies.first().cloned()
54 }
55
56 /// Iterate over the [`Body`] in the [`Bodies`]
57 ///
58 /// # Examples
59 ///
60 /// ```
61 /// use mit_commit::{Bodies, Body};
62 /// let bodies = Bodies::from(vec![
63 /// Body::from("Body 1"),
64 /// Body::from("Body 2"),
65 /// Body::from("Body 3"),
66 /// ]);
67 /// let mut iterator = bodies.iter();
68 ///
69 /// assert_eq!(iterator.next(), Some(&Body::from("Body 1")));
70 /// assert_eq!(iterator.next(), Some(&Body::from("Body 2")));
71 /// assert_eq!(iterator.next(), Some(&Body::from("Body 3")));
72 /// assert_eq!(iterator.next(), None);
73 /// ```
74 pub fn iter(&self) -> Iter<'_, Body<'_>> {
75 self.bodies.iter()
76 }
77}
78
79impl<'a> IntoIterator for Bodies<'a> {
80 type IntoIter = IntoIter<Body<'a>>;
81 type Item = Body<'a>;
82
83 /// Iterate over the [`Body`] in the [`Bodies`]
84 ///
85 /// # Examples
86 ///
87 /// ```
88 /// use mit_commit::{Bodies, Body};
89 /// let bodies = Bodies::from(vec![
90 /// Body::from("Body 1"),
91 /// Body::from("Body 2"),
92 /// Body::from("Body 3"),
93 /// ]);
94 /// let mut iterator = bodies.into_iter();
95 ///
96 /// assert_eq!(iterator.next(), Some(Body::from("Body 1")));
97 /// assert_eq!(iterator.next(), Some(Body::from("Body 2")));
98 /// assert_eq!(iterator.next(), Some(Body::from("Body 3")));
99 /// assert_eq!(iterator.next(), None);
100 /// ```
101 fn into_iter(self) -> Self::IntoIter {
102 self.bodies.into_iter()
103 }
104}
105
106impl<'a> IntoIterator for &'a Bodies<'a> {
107 type IntoIter = Iter<'a, Body<'a>>;
108 type Item = &'a Body<'a>;
109
110 /// Iterate over the [`Body`] in the [`Bodies`]
111 ///
112 /// # Examples
113 ///
114 /// ```
115 /// use std::borrow::Borrow;
116 ///
117 /// use mit_commit::{Bodies, Body};
118 /// let bodies = Bodies::from(vec![
119 /// Body::from("Body 1"),
120 /// Body::from("Body 2"),
121 /// Body::from("Body 3"),
122 /// ]);
123 /// let bodies_ref = bodies.borrow();
124 /// let mut iterator = bodies_ref.into_iter();
125 ///
126 /// assert_eq!(iterator.next(), Some(&Body::from("Body 1")));
127 /// assert_eq!(iterator.next(), Some(&Body::from("Body 2")));
128 /// assert_eq!(iterator.next(), Some(&Body::from("Body 3")));
129 /// assert_eq!(iterator.next(), None);
130 /// ```
131 fn into_iter(self) -> Self::IntoIter {
132 self.bodies.iter()
133 }
134}
135
136impl<'a> Display for Bodies<'a> {
137 /// Render the [`Bodies`] as text
138 ///
139 /// # Examples
140 ///
141 /// ```
142 /// use mit_commit::{Bodies, Body};
143 /// let bodies = Bodies::from(vec![
144 /// Body::from("Body 1"),
145 /// Body::from("Body 2"),
146 /// Body::from("Body 3"),
147 /// ]);
148 ///
149 /// assert_eq!(format!("{}", bodies), "Body 1\n\nBody 2\n\nBody 3");
150 /// ```
151 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
152 write!(f, "{}", String::from(self.clone()))
153 }
154}
155
156impl<'a> From<Vec<Body<'a>>> for Bodies<'a> {
157 /// Combine a [`Vec`] of [`Body`] into [`Bodies`]
158 ///
159 /// # Examples
160 ///
161 /// ```
162 /// use mit_commit::{Bodies, Body};
163 /// let bodies = Bodies::from(vec![
164 /// Body::from("Body 1"),
165 /// Body::from("Body 2"),
166 /// Body::from("Body 3"),
167 /// ]);
168 /// let mut iterator = bodies.into_iter();
169 ///
170 /// assert_eq!(iterator.next(), Some(Body::from("Body 1")));
171 /// assert_eq!(iterator.next(), Some(Body::from("Body 2")));
172 /// assert_eq!(iterator.next(), Some(Body::from("Body 3")));
173 /// assert_eq!(iterator.next(), None);
174 /// ```
175 fn from(bodies: Vec<Body<'a>>) -> Self {
176 Self { bodies }
177 }
178}
179
180impl<'a> From<Bodies<'a>> for String {
181 fn from(bodies: Bodies<'_>) -> Self {
182 bodies
183 .bodies
184 .into_iter()
185 .map(Self::from)
186 .collect::<Vec<_>>()
187 .join("\n\n")
188 }
189}
190
191impl<'a> From<Vec<Fragment<'a>>> for Bodies<'a> {
192 fn from(bodies: Vec<Fragment<'a>>) -> Self {
193 let raw_body = bodies
194 .iter()
195 .filter_map(|values| {
196 if let Fragment::Body(body) = values {
197 Some(body.clone())
198 } else {
199 None
200 }
201 })
202 .collect::<Vec<_>>();
203
204 let trailer_count = raw_body
205 .clone()
206 .into_iter()
207 .skip(1)
208 .rev()
209 .take_while(|body| body.is_empty() || Trailer::try_from(body.clone()).is_ok())
210 .count();
211 let mut non_trailer_item_count = raw_body.len() - trailer_count;
212 non_trailer_item_count = non_trailer_item_count.saturating_sub(1);
213
214 raw_body
215 .into_iter()
216 .enumerate()
217 .skip(1)
218 .take(non_trailer_item_count)
219 .map(|(_, body)| body)
220 .collect::<Vec<Body<'_>>>()
221 .into()
222 }
223}