mit_commit/comments.rs
1use std::slice::Iter;
2
3use crate::{comment::Comment, fragment::Fragment};
4
5/// A collection of comments from a [`CommitMessage`]
6#[derive(Debug, PartialEq, Eq, Clone, Default)]
7pub struct Comments<'a> {
8 comments: Vec<Comment<'a>>,
9}
10
11impl Comments<'_> {
12 /// Iterate over the [`Comment`] in the [`Comments`]
13 ///
14 /// # Returns
15 ///
16 /// An iterator over the comments in this collection
17 ///
18 /// # Examples
19 ///
20 /// ```
21 /// use mit_commit::{Comment, Comments};
22 ///
23 /// let trailers = Comments::from(vec![
24 /// Comment::from("# Comment 1"),
25 /// Comment::from("# Comment 2"),
26 /// Comment::from("# Comment 3"),
27 /// ]);
28 /// let mut iterator = trailers.iter();
29 ///
30 /// assert_eq!(iterator.next(), Some(&Comment::from("# Comment 1")));
31 /// assert_eq!(iterator.next(), Some(&Comment::from("# Comment 2")));
32 /// assert_eq!(iterator.next(), Some(&Comment::from("# Comment 3")));
33 /// assert_eq!(iterator.next(), None);
34 /// ```
35 pub fn iter(&self) -> Iter<'_, Comment<'_>> {
36 self.comments.iter()
37 }
38}
39
40impl<'a> IntoIterator for Comments<'a> {
41 type IntoIter = std::vec::IntoIter<Comment<'a>>;
42 type Item = Comment<'a>;
43
44 /// Iterate over the [`Comment`] in the [`Comments`]
45 ///
46 /// # Returns
47 ///
48 /// An iterator that takes ownership of the comments
49 ///
50 /// # Examples
51 ///
52 /// ```
53 /// use mit_commit::{Comment, Comments};
54 ///
55 /// let trailers = Comments::from(vec![
56 /// Comment::from("# Comment 1"),
57 /// Comment::from("# Comment 2"),
58 /// Comment::from("# Comment 3"),
59 /// ]);
60 /// let mut iterator = trailers.into_iter();
61 ///
62 /// assert_eq!(iterator.next(), Some(Comment::from("# Comment 1")));
63 /// assert_eq!(iterator.next(), Some(Comment::from("# Comment 2")));
64 /// assert_eq!(iterator.next(), Some(Comment::from("# Comment 3")));
65 /// assert_eq!(iterator.next(), None);
66 /// ```
67 fn into_iter(self) -> Self::IntoIter {
68 self.comments.into_iter()
69 }
70}
71
72impl<'a> IntoIterator for &'a Comments<'a> {
73 type IntoIter = Iter<'a, Comment<'a>>;
74 type Item = &'a Comment<'a>;
75
76 /// Iterate over the [`Comment`] in the [`Comments`]
77 ///
78 /// # Returns
79 ///
80 /// An iterator over references to the comments
81 ///
82 /// # Examples
83 ///
84 /// ```
85 /// use std::borrow::Borrow;
86 ///
87 /// use mit_commit::{Comment, Comments};
88 ///
89 /// let comments = Comments::from(vec![
90 /// Comment::from("# Comment 1"),
91 /// Comment::from("# Comment 2"),
92 /// Comment::from("# Comment 3"),
93 /// ]);
94 /// let comments_ref = comments.borrow();
95 /// let mut iterator = comments_ref.into_iter();
96 ///
97 /// assert_eq!(iterator.next(), Some(&Comment::from("# Comment 1")));
98 /// assert_eq!(iterator.next(), Some(&Comment::from("# Comment 2")));
99 /// assert_eq!(iterator.next(), Some(&Comment::from("# Comment 3")));
100 /// assert_eq!(iterator.next(), None);
101 /// ```
102 fn into_iter(self) -> Self::IntoIter {
103 self.comments.iter()
104 }
105}
106
107impl<'a> From<Vec<Comment<'a>>> for Comments<'a> {
108 /// Create Comments from a vector of Comment
109 ///
110 /// # Arguments
111 ///
112 /// * `comments` - The vector of comments to create the collection from
113 ///
114 /// # Returns
115 ///
116 /// A new Comments collection containing the provided comments
117 fn from(comments: Vec<Comment<'a>>) -> Self {
118 Self { comments }
119 }
120}
121
122impl From<Comments<'_>> for String {
123 /// Convert Comments to a String
124 ///
125 /// # Arguments
126 ///
127 /// * `comments` - The comments collection to convert
128 ///
129 /// # Returns
130 ///
131 /// A String containing all comments joined with double newlines
132 fn from(comments: Comments<'_>) -> Self {
133 comments
134 .comments
135 .into_iter()
136 .map(Self::from)
137 .collect::<Vec<_>>()
138 .join("\n\n")
139 }
140}
141
142impl<'a> From<Vec<Fragment<'a>>> for Comments<'a> {
143 /// Create Comments from a vector of Fragment
144 ///
145 /// # Arguments
146 ///
147 /// * `ast` - The vector of fragments to filter for comments
148 ///
149 /// # Returns
150 ///
151 /// A new Comments collection containing only the Comment fragments
152 fn from(ast: Vec<Fragment<'a>>) -> Self {
153 ast.into_iter()
154 .filter_map(|values| {
155 if let Fragment::Comment(comment) = values {
156 Some(comment)
157 } else {
158 None
159 }
160 })
161 .collect::<Vec<Comment<'_>>>()
162 .into()
163 }
164}
165
166#[cfg(test)]
167mod tests {
168 use indoc::indoc;
169
170 use super::*;
171 use crate::body::Body;
172
173 #[test]
174 fn test_iterator_implementation() {
175 let comments = Comments::from(vec![
176 Comment::from("# Comment 1"),
177 Comment::from("# Comment 2"),
178 Comment::from("# Comment 3"),
179 ]);
180 let mut iterator = comments.iter();
181
182 assert_eq!(
183 iterator.next(),
184 Some(&Comment::from("# Comment 1")),
185 "Iterator should return the first comment"
186 );
187 assert_eq!(
188 iterator.next(),
189 Some(&Comment::from("# Comment 2")),
190 "Iterator should return the second comment"
191 );
192 assert_eq!(
193 iterator.next(),
194 Some(&Comment::from("# Comment 3")),
195 "Iterator should return the third comment"
196 );
197 assert_eq!(
198 iterator.next(),
199 None,
200 "Iterator should return None after all comments"
201 );
202 }
203
204 #[test]
205 fn test_string_conversion() {
206 let comments = Comments::from(vec![
207 Comment::from("# Message Body"),
208 Comment::from("# Another Message Body"),
209 ]);
210
211 assert_eq!(
212 String::from(comments),
213 String::from(indoc!(
214 "
215 # Message Body
216
217 # Another Message Body"
218 )),
219 "Comments should convert to a string with comments separated by double newlines"
220 );
221 }
222
223 #[test]
224 fn test_creation_from_fragments() {
225 let comments = Comments::from(vec![
226 Fragment::Comment(Comment::from("# Message Body")),
227 Fragment::Body(Body::from("Some body content")),
228 Fragment::Comment(Comment::from("# Another Message Body")),
229 ]);
230
231 assert_eq!(
232 comments,
233 Comments::from(vec![
234 Comment::from("# Message Body"),
235 Comment::from("# Another Message Body"),
236 ]),
237 "Comments should be created from fragments, filtering out non-comment fragments"
238 );
239 }
240}