use std::fmt; pub struct HelpMessage { title: &'static str, segments: Vec, } impl fmt::Display for HelpMessage { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!(f, "{}", self.title.to_uppercase())?; for seg in &self.segments { write!(f, "{seg}")?; } Ok(()) } } pub struct HelpSegment { head: &'static str, body: &'static str, } impl fmt::Display for HelpSegment { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!(f, "{}:", self.head)?; const INDENT: IndentStr<4> = IndentStr::new(); const LINE_LEN: usize = 80 - INDENT.len(); let mut curr = self.body; loop { let line_len = curr.len().min(LINE_LEN); let line = &curr[..line_len]; curr = &curr[line_len..]; writeln!(f, "{INDENT}{line}")?; if curr.len() == 0 { break Ok(()); } } } } struct IndentStr { arr: [u8; N], } impl IndentStr { pub const fn new() -> Self { IndentStr { arr: [' ' as u8; N], } } pub const fn len(&self) -> usize { N } pub const fn as_str(&self) -> &str { unsafe { std::str::from_utf8_unchecked(&self.arr) } } } impl Default for IndentStr { fn default() -> Self { Self::new() } } impl fmt::Display for IndentStr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let ident = unsafe { std::str::from_utf8_unchecked(&self.arr) }; write!(f, "{ident}") } } #[cfg(test)] mod test { use crate::helpgen::HelpSegment; #[test] fn segment() { let segment = HelpSegment { head: "head", body: "body of the help string", }; let expected = "head:\n body of the help string\n"; assert_eq!(segment.to_string(), expected) } #[test] fn segment_longline() { let segment = HelpSegment { head: "head", body: "01234567890123456789012345678901234567890123456789012345678901234567890123456789", }; let expected = "head:\n 0123456789012345678901234567890123456789012345678901234567890123456789012345\n 6789\n"; assert_eq!(segment.to_string(), expected) } }