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}