pub mod components; pub mod views; use iocraft::prelude::*; pub fn render_element_to_string(element: &mut E, no_color: bool) -> String { let canvas = element.render(None); let mut bytes = Vec::new(); if no_color { canvas .write(&mut bytes) .expect("writing iocraft canvas should not fail"); } else { canvas .write_ansi(&mut bytes) .expect("writing ANSI iocraft canvas should not fail"); } let s = String::from_utf8(bytes).expect("iocraft output should be UTF-8"); let s = s.replace("\u{1b}[K", ""); // the ANSI writer ends a row with \r\n as for a terminal in raw mode // a \r left over shows as ^M let mut lines = s .split('\n') .map(|line| line.trim_end_matches(['\r', ' ']).to_string()) .collect::>(); while lines.last().map(|line| line.is_empty()).unwrap_or(false) { lines.pop(); } lines.join("\n") } #[cfg(test)] mod tests { use super::*; #[test] fn a_note_passes_the_colored_output_without_an_escape_sequence() { let mut element = element! { View(flex_direction: FlexDirection::Column) { Text(content: crate::common::note_lines("plan \u{1b}\u{1b}[31m[8mconcealed\rover")) } }; let shown = crate::common::printable(&render_element_to_string(&mut element, false)); assert!(!shown.contains("\u{1b}[8m"), "{shown:?}"); assert_eq!(shown.lines().count(), 1, "{shown:?}"); } #[test] fn colored_rows_end_without_a_carriage_return() { let mut element = element! { View(flex_direction: FlexDirection::Column) { Text(content: "Today", color: Color::Yellow) Text(content: "Weekly review") } }; let colored = render_element_to_string(&mut element, false); assert!(!colored.contains('\r'), "{colored:?}"); assert_eq!(colored.lines().count(), 2, "{colored:?}"); assert_eq!( render_element_to_string(&mut element, true), "Today\nWeekly review" ); } }