molten_sigil/
lib.rs

1//! # Sigil ✨
2//!
3//! Human-readable ANSI escape sequences for terminal styling.
4//!
5//! Sigil makes it easy to work with ANSI escape codes in a type-safe,
6//! readable way. It's the Rust equivalent of [sequin](https://github.com/charmbracelet/sequin)
7//! from Charmbracelet.
8//!
9//! ## Quick Start
10//!
11//! ```rust
12//! use molten_sigil::{style, Color, Modifier};
13//!
14//! // Style some text
15//! let styled = style("Hello, World!")
16//!     .fg(Color::Red)
17//!     .bold()
18//!     .to_string();
19//!
20//! println!("{}", styled);
21//!
22//! // Use RGB colors
23//! let rgb = style("Molten Orange")
24//!     .fg(Color::rgb(249, 115, 22))
25//!     .to_string();
26//!
27//! // Parse existing ANSI sequences
28//! let parsed = molten_sigil::parse("\x1b[31mRed text\x1b[0m");
29//! ```
30//!
31//! ## Features
32//!
33//! - **Type-safe** - No string manipulation, just types
34//! - **Human-readable** - Parse ANSI codes to readable descriptions
35//! - **Zero-copy** - Efficient string handling where possible
36//! - **Brand integration** - Optional Molten brand colors via `brand` feature
37
38#![deny(missing_docs)]
39#![deny(clippy::all)]
40#![warn(clippy::pedantic)]
41#![allow(clippy::module_name_repetitions)]
42
43mod color;
44mod escape;
45mod modifier;
46mod parser;
47mod sequence;
48mod style;
49
50pub use color::Color;
51#[cfg(feature = "brand")]
52pub use color::brand;
53pub use escape::{Escape, EscapeKind};
54pub use modifier::Modifier;
55pub use parser::{parse, strip_ansi, visible_len, ParsedSequence};
56pub use sequence::{Sequence, SequenceBuilder};
57pub use style::{style, Style, Styled};
58
59/// CSI (Control Sequence Introducer) prefix.
60pub const CSI: &str = "\x1b[";
61
62/// OSC (Operating System Command) prefix.
63pub const OSC: &str = "\x1b]";
64
65/// SGR (Select Graphic Rendition) suffix.
66pub const SGR_SUFFIX: &str = "m";
67
68/// Reset all attributes.
69pub const RESET: &str = "\x1b[0m";
70
71/// Common escape sequences.
72pub mod sequences {
73    /// Clear the entire screen.
74    pub const CLEAR_SCREEN: &str = "\x1b[2J";
75
76    /// Clear from cursor to end of screen.
77    pub const CLEAR_TO_END: &str = "\x1b[0J";
78
79    /// Clear from cursor to start of screen.
80    pub const CLEAR_TO_START: &str = "\x1b[1J";
81
82    /// Clear the entire line.
83    pub const CLEAR_LINE: &str = "\x1b[2K";
84
85    /// Clear from cursor to end of line.
86    pub const CLEAR_LINE_TO_END: &str = "\x1b[0K";
87
88    /// Clear from cursor to start of line.
89    pub const CLEAR_LINE_TO_START: &str = "\x1b[1K";
90
91    /// Move cursor to home position (0, 0).
92    pub const CURSOR_HOME: &str = "\x1b[H";
93
94    /// Hide cursor.
95    pub const CURSOR_HIDE: &str = "\x1b[?25l";
96
97    /// Show cursor.
98    pub const CURSOR_SHOW: &str = "\x1b[?25h";
99
100    /// Save cursor position.
101    pub const CURSOR_SAVE: &str = "\x1b[s";
102
103    /// Restore cursor position.
104    pub const CURSOR_RESTORE: &str = "\x1b[u";
105
106    /// Enter alternate screen buffer.
107    pub const ALT_SCREEN_ENTER: &str = "\x1b[?1049h";
108
109    /// Exit alternate screen buffer.
110    pub const ALT_SCREEN_EXIT: &str = "\x1b[?1049l";
111
112    /// Enable mouse tracking.
113    pub const MOUSE_ENABLE: &str = "\x1b[?1000h";
114
115    /// Disable mouse tracking.
116    pub const MOUSE_DISABLE: &str = "\x1b[?1000l";
117
118    /// Enable bracketed paste mode.
119    pub const BRACKETED_PASTE_ENABLE: &str = "\x1b[?2004h";
120
121    /// Disable bracketed paste mode.
122    pub const BRACKETED_PASTE_DISABLE: &str = "\x1b[?2004l";
123}
124
125/// Cursor movement helpers.
126pub mod cursor {
127    /// Move cursor up by n rows.
128    #[must_use]
129    pub fn up(n: u16) -> String {
130        format!("\x1b[{n}A")
131    }
132
133    /// Move cursor down by n rows.
134    #[must_use]
135    pub fn down(n: u16) -> String {
136        format!("\x1b[{n}B")
137    }
138
139    /// Move cursor right by n columns.
140    #[must_use]
141    pub fn right(n: u16) -> String {
142        format!("\x1b[{n}C")
143    }
144
145    /// Move cursor left by n columns.
146    #[must_use]
147    pub fn left(n: u16) -> String {
148        format!("\x1b[{n}D")
149    }
150
151    /// Move cursor to specific position (1-indexed).
152    #[must_use]
153    pub fn goto(row: u16, col: u16) -> String {
154        format!("\x1b[{row};{col}H")
155    }
156
157    /// Move cursor to specific column (1-indexed).
158    #[must_use]
159    pub fn column(col: u16) -> String {
160        format!("\x1b[{col}G")
161    }
162}
163
164/// Prelude for convenient imports.
165pub mod prelude {
166    pub use crate::color::Color;
167    pub use crate::modifier::Modifier;
168    pub use crate::style::{style, Style, Styled};
169    pub use crate::{cursor, sequences, RESET};
170}
171
172#[cfg(test)]
173mod tests {
174    use super::*;
175
176    #[test]
177    fn test_style_basic() {
178        let s = style("test").fg(Color::Red).to_string();
179        assert!(s.contains("\x1b["));
180        assert!(s.contains("test"));
181        assert!(s.ends_with(RESET));
182    }
183
184    #[test]
185    fn test_cursor_movement() {
186        assert_eq!(cursor::up(5), "\x1b[5A");
187        assert_eq!(cursor::goto(10, 20), "\x1b[10;20H");
188    }
189}