use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct CSharpComment(String);
impl CSharpComment {
pub(crate) fn from_str_option(opt: Option<&str>) -> Option<Self> {
let text = opt?;
if text.trim().is_empty() {
return None;
}
Some(Self(xml_escape(text)))
}
pub(crate) fn lines(&self) -> std::str::Lines<'_> {
self.0.lines()
}
}
impl fmt::Display for CSharpComment {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
fn xml_escape(text: &str) -> String {
let mut out = String::with_capacity(text.len());
for c in text.chars() {
match c {
'&' => out.push_str("&"),
'<' => out.push_str("<"),
'>' => out.push_str(">"),
other => out.push(other),
}
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn from_str_option_returns_none_when_input_is_none() {
assert!(CSharpComment::from_str_option(None).is_none());
}
#[test]
fn from_str_option_returns_none_for_whitespace_only() {
assert!(CSharpComment::from_str_option(Some(" \n ")).is_none());
}
#[test]
fn from_str_option_escapes_xml_special_characters() {
let comment = CSharpComment::from_str_option(Some("Wraps Vec<T> & co.")).unwrap();
assert_eq!(comment.to_string(), "Wraps Vec<T> & co.");
}
#[test]
fn lines_yields_each_line_including_blank_separators() {
let comment = CSharpComment::from_str_option(Some("First.\n\nSecond.")).unwrap();
let collected: Vec<&str> = comment.lines().collect();
assert_eq!(collected, vec!["First.", "", "Second."]);
}
}