#[derive(Debug, Default, Clone, PartialEq, Eq)]
#[repr(transparent)]
pub struct Body(Option<String>);
impl<T: ToString> From<T> for Body {
fn from(value: T) -> Self {
let value = value.to_string();
let lines: Vec<&str> = value
.trim_end()
.lines()
.map(|line| line.trim_end())
.skip_while(|line| line.is_empty())
.collect();
match lines.join("\n").as_str() {
"" => Self::default(),
value => Self(Some(value.into())),
}
}
}
impl Body {
pub fn format(&self) -> String {
match &self.0 {
None => String::new(),
Some(value) if value.trim().is_empty() => String::new(),
Some(body) => format!("\n{body}\n"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_produces_none() {
assert_eq!(Body::default(), Body(None));
}
#[test]
fn from_empty_string_produces_none() {
assert_eq!(Body::from(""), Body(None));
}
#[test]
fn from_whitespace_only_produces_none() {
assert_eq!(Body::from(" "), Body(None));
}
#[test]
fn from_tab_and_newline_only_produces_none() {
assert_eq!(Body::from("\t\n "), Body(None));
}
#[test]
fn from_single_newline_produces_none() {
assert_eq!(Body::from("\n"), Body(None));
}
#[test]
fn from_non_empty_string_produces_some() {
assert_eq!(
Body::from("some body text"),
Body(Some("some body text".to_string())),
);
}
#[test]
fn from_preserves_leading_whitespace() {
assert_eq!(
Body::from(" content "),
Body(Some(" content".to_string())),
);
}
#[test]
fn from_preserves_per_line_leading_whitespace() {
let input = "- item one\n - nested item\n - deeply nested";
assert_eq!(Body::from(input), Body(Some(input.to_string())),);
}
#[test]
fn from_trims_trailing_newline() {
assert_eq!(
Body::from("editor content\n"),
Body(Some("editor content".to_string())),
);
}
#[test]
fn from_drops_leading_blank_lines() {
assert_eq!(Body::from("\n\ncontent"), Body(Some("content".to_string())),);
}
#[test]
fn from_normalises_crlf_to_lf() {
assert_eq!(
Body::from("line one\r\nline two"),
Body(Some("line one\nline two".to_string())),
);
}
#[test]
fn from_preserves_internal_newlines() {
assert_eq!(
Body::from("line one\nline two"),
Body(Some("line one\nline two".to_string())),
);
}
#[test]
fn into_conversion_works() {
let body: Body = "content".into();
assert_eq!(body, Body(Some("content".to_string())));
}
#[test]
fn clone_produces_equal_value() {
let body = Body::from("content");
assert_eq!(body.clone(), body);
}
#[test]
fn equality_same_content() {
assert_eq!(Body::from("same"), Body::from("same"));
}
#[test]
fn inequality_different_content() {
assert_ne!(Body::from("first"), Body::from("second"));
}
#[test]
fn inequality_none_vs_some() {
assert_ne!(Body::default(), Body::from("content"));
}
#[test]
fn debug_output_is_available() {
let body = Body::from("test");
assert!(format!("{:?}", body).contains("Body"));
}
#[test]
fn format_none_returns_empty_string() {
assert_eq!(Body::default().format(), "");
}
#[test]
fn format_some_returns_newline_wrapped_content() {
let body = Body::from("some body text");
assert_eq!(body.format(), "\nsome body text\n");
}
#[test]
fn format_some_multiline_preserves_content() {
let body = Body::from("line one\nline two");
assert_eq!(body.format(), "\nline one\nline two\n");
}
}