mit_commit/body.rs
1use std::{
2 borrow::Cow,
3 fmt,
4 fmt::{Display, Formatter},
5};
6
7/// A single contiguous block of [`CommitMessage`] text
8#[derive(Debug, PartialEq, Eq, Clone, Default)]
9pub struct Body<'a> {
10 text: Cow<'a, str>,
11}
12
13impl Body<'_> {
14 /// Append one [`Body`] onto another
15 ///
16 /// # Arguments
17 ///
18 /// * `additional` - The body to append to this one
19 ///
20 /// # Returns
21 ///
22 /// A new body with the content of both bodies separated by a newline
23 ///
24 /// # Examples
25 ///
26 /// ```
27 /// use indoc::indoc;
28 /// use mit_commit::Body;
29 ///
30 /// assert_eq!(
31 /// Body::from(indoc!(
32 /// "
33 /// Example 1
34 /// Example 2"
35 /// )),
36 /// Body::from("Example 1").append(&Body::from("Example 2"))
37 /// )
38 /// ```
39 #[must_use]
40 pub fn append(&self, additional: &Self) -> Self {
41 Self::from(format!("{}\n{}", self.text, additional.text))
42 }
43
44 /// Checks if this [`Body`] is empty
45 ///
46 /// # Returns
47 ///
48 /// `true` if the body is empty, `false` otherwise
49 ///
50 /// # Examples
51 ///
52 /// ```
53 /// use mit_commit::Body;
54 ///
55 /// assert_eq!(Body::from("").is_empty(), true);
56 /// assert_eq!(Body::from("not empty").is_empty(), false);
57 /// ```
58 #[must_use]
59 pub fn is_empty(&self) -> bool {
60 self.text.is_empty()
61 }
62}
63
64impl<'a> From<Cow<'a, str>> for Body<'a> {
65 /// Create a Body from a Cow<_, str>
66 ///
67 /// # Arguments
68 ///
69 /// * `body` - The string content to create the body from
70 ///
71 /// # Returns
72 ///
73 /// A new Body containing the provided string
74 ///
75 /// # Examples
76 ///
77 /// ```
78 /// use std::borrow::Cow;
79 ///
80 /// use mit_commit::Body;
81 ///
82 /// let expected = "a string";
83 /// let input = Cow::from(expected);
84 /// assert_eq!(Body::from(input).to_string(), expected)
85 /// ```
86 fn from(body: Cow<'a, str>) -> Self {
87 Self { text: body }
88 }
89}
90
91impl<'a> From<&'a str> for Body<'a> {
92 /// Create a Body from a string slice
93 ///
94 /// # Arguments
95 ///
96 /// * `body` - The string slice to create the body from
97 ///
98 /// # Returns
99 ///
100 /// A new Body containing the provided string
101 fn from(body: &'a str) -> Self {
102 Self::from(Cow::Borrowed(body))
103 }
104}
105
106impl From<String> for Body<'_> {
107 /// Create a Body from a String
108 ///
109 /// # Arguments
110 ///
111 /// * `body` - The string to create the body from
112 ///
113 /// # Returns
114 ///
115 /// A new Body containing the provided string
116 fn from(body: String) -> Self {
117 Self::from(Cow::from(body))
118 }
119}
120
121impl From<Body<'_>> for String {
122 /// Convert a Body to a String
123 ///
124 /// # Arguments
125 ///
126 /// * `body` - The body to convert
127 ///
128 /// # Returns
129 ///
130 /// A String containing the body's text
131 fn from(body: Body<'_>) -> Self {
132 body.text.into()
133 }
134}
135
136impl<'a> From<Body<'a>> for Cow<'a, str> {
137 /// Convert a Body to a Cow<_, str>
138 ///
139 /// # Arguments
140 ///
141 /// * `body` - The body to convert
142 ///
143 /// # Returns
144 ///
145 /// A Cow<_, str> containing the body's text
146 ///
147 /// # Examples
148 ///
149 /// ```
150 /// use std::borrow::Cow;
151 ///
152 /// use mit_commit::Body;
153 ///
154 /// let expected = Cow::from("a string");
155 /// let input = Body::from(expected.clone());
156 /// assert_eq!(Cow::from(input), expected)
157 /// ```
158 fn from(body: Body<'a>) -> Self {
159 body.text
160 }
161}
162
163impl Display for Body<'_> {
164 /// Format the Body for display
165 ///
166 /// # Arguments
167 ///
168 /// * `f` - The formatter to write to
169 ///
170 /// # Returns
171 ///
172 /// A Result indicating whether the operation succeeded
173 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
174 write!(f, "{}", String::from(self.clone()))
175 }
176}
177
178#[cfg(test)]
179mod tests {
180 use indoc::indoc;
181
182 use super::*;
183
184 #[test]
185 fn test_string_conversion_from_str() {
186 let body = Body::from("Example Body");
187
188 assert_eq!(
189 String::from(body),
190 String::from("Example Body"),
191 "Body should convert to the correct string when created from a str"
192 );
193 }
194
195 #[test]
196 fn test_string_conversion_from_string() {
197 let body = Body::from(String::from("Example Body"));
198
199 assert_eq!(
200 String::from(body),
201 String::from("Example Body"),
202 "Body should convert to the correct string when created from a String"
203 );
204 }
205
206 #[test]
207 fn test_display_implementation() {
208 let body = Body::from("Example Body");
209
210 assert_eq!(
211 format!("{body}"),
212 "Example Body",
213 "Display implementation should format the body correctly"
214 );
215 }
216
217 #[test]
218 fn test_append_body_fragments() {
219 assert_eq!(
220 Body::from(indoc!(
221 "
222 Example 1
223 Example 2"
224 )),
225 Body::from("Example 1").append(&Body::from("Example 2")),
226 "Appending bodies should create a new body with content separated by newline"
227 );
228 }
229
230 #[test]
231 fn test_is_empty_with_empty_body() {
232 assert!(
233 Body::from("").is_empty(),
234 "Empty body should be identified as empty"
235 );
236 }
237
238 #[test]
239 fn test_is_empty_with_non_empty_body() {
240 assert!(
241 !Body::from("something").is_empty(),
242 "Non-empty body should not be identified as empty"
243 );
244 }
245}