#[cfg(not(feature = "std"))]
use alloc::{string::String, vec::Vec};
pub fn indent(text: &str, spaces: usize) -> String {
let prefix = " ".repeat(spaces);
text.lines()
.map(|line| {
if line.is_empty() {
String::new()
} else {
#[cfg(feature = "std")]
{
format!("{}{}", prefix, line)
}
#[cfg(not(feature = "std"))]
{
use alloc::format;
format!("{}{}", prefix, line)
}
}
})
.collect::<Vec<_>>()
.join("\n")
}
#[allow(dead_code)]
pub fn wrap_text(text: &str, width: usize) -> Vec<String> {
let mut lines = Vec::new();
let mut current_line = String::new();
let mut current_len = 0;
for word in text.split_whitespace() {
let word_len = word.len();
if current_len + word_len + 1 > width && !current_line.is_empty() {
lines.push(current_line);
current_line = String::new();
current_len = 0;
}
if !current_line.is_empty() {
current_line.push(' ');
current_len += 1;
}
current_line.push_str(word);
current_len += word_len;
}
if !current_line.is_empty() {
lines.push(current_line);
}
lines
}
#[allow(dead_code)]
pub fn has_ansi_codes(s: &str) -> bool {
s.contains('\x1b')
}
#[allow(dead_code)]
pub fn strip_ansi_codes(s: &str) -> String {
let mut result = String::new();
let mut in_escape = false;
for ch in s.chars() {
if ch == '\x1b' {
in_escape = true;
} else if in_escape {
if ch == 'm' {
in_escape = false;
}
} else {
result.push(ch);
}
}
result
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_indent() {
let text = "line1\nline2\nline3";
let indented = indent(text, 2);
assert_eq!(indented, " line1\n line2\n line3");
}
#[test]
fn test_wrap_text() {
let text = "This is a long line that should be wrapped";
let wrapped = wrap_text(text, 20);
assert!(wrapped.len() > 1);
assert!(wrapped.iter().all(|line| line.len() <= 20));
}
#[test]
fn test_strip_ansi_codes() {
let colored = "\x1b[31mRed text\x1b[0m";
let stripped = strip_ansi_codes(colored);
assert_eq!(stripped, "Red text");
}
}