wrap-ansi

Crates.iowrap-ansi
lib.rswrap-ansi
version0.1.0
created_at2025-09-08 09:44:05.921647+00
updated_at2025-09-08 09:44:05.921647+00
descriptionA high-performance, Unicode-aware Rust library for intelligently wrapping text while preserving ANSI escape sequences, colors, styles, and hyperlinks
homepagehttps://github.com/sabry-awad97/wrap-ansi
repositoryhttps://github.com/sabry-awad97/wrap-ansi
max_upload_size
id1829051
size95,327
Sabry Awad (sabry-awad97)

documentation

https://docs.rs/wrap-ansi

README

🎨 wrap-ansi

Crates.io Documentation License Build Status

A high-performance, Unicode-aware Rust library for intelligently wrapping text while preserving ANSI escape sequences, colors, styles, and hyperlinks.

This library is a faithful Rust port of the popular JavaScript wrap-ansi library, designed for terminal applications, CLI tools, and any software that needs to display formatted text in constrained widths.

✨ Key Features

Feature Description
🎨 ANSI-Aware Wrapping Preserves colors, styles, and formatting across line breaks
πŸ”— Hyperlink Support Maintains clickable OSC 8 hyperlinks when wrapping
🌍 Unicode Ready Correctly handles CJK characters, emojis, and combining marks
⚑ High Performance Optimized algorithms with pre-compiled regex patterns
πŸ› οΈ Flexible Options Hard/soft wrapping, trimming, word boundaries control
πŸ”’ Memory Safe Built-in protection against DoS attacks with input size limits
πŸ“š Rich API Fluent builder pattern and comprehensive error handling

πŸš€ Quick Start

Add this to your Cargo.toml:

[dependencies]
wrap-ansi = "0.1.0"

Basic Text Wrapping

use wrap_ansi::wrap_ansi;

let text = "The quick brown fox jumps over the lazy dog";
let wrapped = wrap_ansi(text, 20, None);
println!("{}", wrapped);
// Output:
// The quick brown fox
// jumps over the lazy
// dog

ANSI Colors & Styles

use wrap_ansi::wrap_ansi;

// Colors are preserved across line breaks!
let colored = "\u{001B}[31mThis is red text that will be wrapped properly\u{001B}[39m";
let wrapped = wrap_ansi(colored, 15, None);
println!("{}", wrapped);
// Each line will have proper color codes: \u{001B}[31m...\u{001B}[39m

Advanced Configuration

use wrap_ansi::{wrap_ansi, WrapOptions};

// Using the builder pattern for clean configuration
let options = WrapOptions::builder()
    .hard_wrap(true)           // Break long words
    .trim_whitespace(false)    // Preserve whitespace
    .word_wrap(true)           // Respect word boundaries
    .build();

let text = "supercalifragilisticexpialidocious word";
let wrapped = wrap_ansi(text, 10, Some(options));
println!("{}", wrapped);
// Output:
// supercalif
// ragilistic
// expialidoc
// ious word

πŸ“– API Reference

Core Functions

wrap_ansi(string, columns, options) -> String

The main wrapping function that handles text with ANSI escape sequences.

Parameters:

  • string: &str - Input text (may contain ANSI escape sequences)
  • columns: usize - Target column width for wrapping
  • options: Option<WrapOptions> - Optional configuration (uses defaults if None)

Returns: Wrapped text with preserved ANSI sequences

wrap_ansi_checked(string, columns, options) -> Result<String, WrapError>

Safe version with comprehensive error handling and input validation.

Parameters: Same as wrap_ansi

Returns:

  • Ok(String) - Successfully wrapped text
  • Err(WrapError) - Detailed error information

Errors:

  • InvalidColumnWidth(usize) - Column width must be > 0
  • InputTooLarge(usize, usize) - Input exceeds 10MB limit

Configuration Options

WrapOptions

Fine-grained control over wrapping behavior:

pub struct WrapOptions {
    pub trim: bool,      // Remove leading/trailing whitespace (default: true)
    pub hard: bool,      // Break long words at boundary (default: false)
    pub word_wrap: bool, // Respect word boundaries (default: true)
}

WrapOptionsBuilder

Fluent builder interface for creating options:

let options = WrapOptions::builder()
    .hard_wrap(true)
    .trim_whitespace(false)
    .word_wrap(true)
    .build();

Builder Methods:

  • .hard_wrap(bool) - Configure hard wrapping behavior
  • .trim_whitespace(bool) - Configure whitespace trimming
  • .word_wrap(bool) - Configure word boundary respect
  • .build() - Build the final WrapOptions

🎯 Use Cases

Perfect for:

  • CLI Applications: Format help text, error messages, and output
  • Terminal UIs: Create responsive layouts that adapt to terminal width
  • Documentation Tools: Generate formatted text with preserved styling
  • Log Processing: Wrap log entries while maintaining color coding
  • Code Formatters: Handle syntax-highlighted code with proper wrapping

🌟 Advanced Features

Wrapping Modes

Mode Behavior Use Case
Soft Wrap (default) Long words move to next line intact Natural text flow
Hard Wrap Long words break at column boundary Strict width constraints
Character Wrap Break anywhere, ignore word boundaries Monospace layouts

ANSI Sequence Support

βœ… Foreground Colors: Standard (30-37) and bright (90-97)
βœ… Background Colors: Standard (40-47) and bright (100-107)
βœ… Text Styles: Bold, italic, underline, strikethrough
βœ… Color Resets: Proper handling of reset sequences (39, 49)
βœ… Hyperlinks: OSC 8 sequences for clickable terminal links
βœ… Custom SGR: Support for any Select Graphic Rendition codes

Unicode & Internationalization

use wrap_ansi::{wrap_ansi, WrapOptions};

// CJK characters are properly counted as 2 columns
let chinese = "δ½ ε₯½δΈ–η•ŒοΌŒθΏ™ζ˜―δΈ€δΈͺζ΅‹θ―•";
let wrapped = wrap_ansi(chinese, 8, None);

// Emojis and combining characters work correctly
let emoji = "Hello πŸ‘‹ World 🌍 with emojis";
let options = WrapOptions::builder().hard_wrap(true).build();
let wrapped = wrap_ansi(emoji, 10, Some(options));

πŸ”§ Error Handling

For applications requiring robust error handling:

use wrap_ansi::{wrap_ansi_checked, WrapError};

let text = "Hello, world!";
match wrap_ansi_checked(text, 0, None) {
    Ok(wrapped) => println!("Wrapped: {}", wrapped),
    Err(WrapError::InvalidColumnWidth(width)) => {
        eprintln!("Invalid width: {}", width);
    }
    Err(WrapError::InputTooLarge(size, max)) => {
        eprintln!("Input too large: {} bytes (max: {})", size, max);
    }
}

πŸ“Š Performance

  • Zero-copy operations where possible
  • Pre-compiled regex patterns for ANSI sequence parsing
  • Efficient string operations with capacity pre-allocation
  • DoS protection with configurable input size limits (10MB default)
  • Minimal allocations through careful memory management

🀝 Compatibility

  • Rust Edition: 2024
  • MSRV: 1.70.0
  • Platforms: All platforms supported by Rust
  • Terminal Compatibility: Works with all ANSI-compatible terminals

πŸ“š Examples

Check out the examples directory for comprehensive usage examples:

cargo run --example improved_features

The example demonstrates:

  • Builder pattern usage
  • Error handling patterns
  • ANSI color preservation
  • Hyperlink support
  • Unicode text handling
  • Complex ANSI sequences

πŸ”— Dependencies

πŸ“„ License

Licensed under either of

at your option.

🀝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

πŸ™ Acknowledgments

This library is inspired by and aims to be compatible with the JavaScript wrap-ansi library by Sindre Sorhus and the Chalk team.

Commit count: 4

cargo fmt