errcraft 0.1.0

Beautiful, structured, and colorful error handling for Rust.
Documentation
//! Utility functions for formatting and text manipulation.

#[cfg(not(feature = "std"))]
use alloc::{string::String, vec::Vec};

/// Indents each line of the input text by the specified amount.
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")
}

/// Wraps text to fit within the specified width.
#[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
}

/// Checks if a string contains ANSI escape codes.
#[allow(dead_code)]
pub fn has_ansi_codes(s: &str) -> bool {
    s.contains('\x1b')
}

/// Strips ANSI escape codes from a string.
#[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");
    }
}