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}