strip_ansi_escapes/
lib.rs1extern crate vte;
28
29use std::io::{self, IntoInnerError, LineWriter, Write};
30use vte::{Parser, Perform};
31
32pub struct Writer<W>
50where
51 W: Write,
52{
53 performer: Performer<W>,
54 parser: Parser,
55}
56
57pub fn strip<T>(data: T) -> Vec<u8>
63where
64 T: AsRef<[u8]>,
65{
66 fn strip_impl(data: &[u8]) -> io::Result<Vec<u8>> {
67 let mut writer = Writer::new(Vec::new());
68 writer.write_all(data)?;
69 Ok(writer.into_inner()?)
70 }
71
72 strip_impl(data.as_ref()).expect("writing to a Vec<u8> cannot fail")
73}
74
75pub fn strip_str<T>(data: T) -> String
85where
86 T: AsRef<str>,
87{
88 let bytes = strip(data.as_ref());
89 String::from_utf8(bytes)
90 .expect("stripping ANSI escapes from a UTF-8 string always results in UTF-8")
91}
92
93struct Performer<W>
94where
95 W: Write,
96{
97 writer: LineWriter<W>,
98 err: Option<io::Error>,
99}
100
101impl<W> Writer<W>
102where
103 W: Write,
104{
105 pub fn new(inner: W) -> Writer<W> {
107 Writer {
108 performer: Performer {
109 writer: LineWriter::new(inner),
110 err: None,
111 },
112 parser: Parser::new(),
113 }
114 }
115
116 pub fn into_inner(self) -> Result<W, IntoInnerError<LineWriter<W>>> {
123 self.performer.into_inner()
124 }
125}
126
127impl<W> Write for Writer<W>
128where
129 W: Write,
130{
131 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
132 self.parser.advance(&mut self.performer, buf);
133 match self.performer.err.take() {
134 Some(e) => Err(e),
135 None => Ok(buf.len()),
136 }
137 }
138
139 fn flush(&mut self) -> io::Result<()> {
140 self.performer.flush()
141 }
142}
143
144impl<W> Performer<W>
145where
146 W: Write,
147{
148 pub fn flush(&mut self) -> io::Result<()> {
149 self.writer.flush()
150 }
151
152 pub fn into_inner(self) -> Result<W, IntoInnerError<LineWriter<W>>> {
153 self.writer.into_inner()
154 }
155}
156
157impl<W> Perform for Performer<W>
158where
159 W: Write,
160{
161 fn print(&mut self, c: char) {
162 self.err = write!(self.writer, "{}", c).err();
164 }
165 fn execute(&mut self, byte: u8) {
166 if byte == b'\n' {
168 self.err = writeln!(self.writer).err();
169 }
170 }
171}
172
173#[cfg(doctest)]
174extern crate doc_comment;
175
176#[cfg(doctest)]
177doc_comment::doctest!("../README.md", readme);
178
179#[cfg(test)]
180mod tests {
181 use super::*;
182
183 fn assert_parsed(input: &[u8], expected: &[u8]) {
184 let bytes = strip(input);
185 assert_eq!(bytes, expected);
186 }
187
188 #[test]
189 fn test_simple() {
190 assert_parsed(b"\x1b[m\x1b[m\x1b[32m\x1b[1m Finished\x1b[m dev [unoptimized + debuginfo] target(s) in 0.0 secs",
191 b" Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs");
192 }
193
194 #[test]
195 fn test_newlines() {
196 assert_parsed(b"foo\nbar\n", b"foo\nbar\n");
197 }
198
199 #[test]
200 fn test_escapes_newlines() {
201 assert_parsed(b"\x1b[m\x1b[m\x1b[32m\x1b[1m Compiling\x1b[m utf8parse v0.1.0
202\x1b[m\x1b[m\x1b[32m\x1b[1m Compiling\x1b[m vte v0.3.2
203\x1b[m\x1b[m\x1b[32m\x1b[1m Compiling\x1b[m strip-ansi-escapes v0.1.0-pre (file:///build/strip-ansi-escapes)
204\x1b[m\x1b[m\x1b[32m\x1b[1m Finished\x1b[m dev [unoptimized + debuginfo] target(s) in 0.66 secs
205",
206 b" Compiling utf8parse v0.1.0
207 Compiling vte v0.3.2
208 Compiling strip-ansi-escapes v0.1.0-pre (file:///build/strip-ansi-escapes)
209 Finished dev [unoptimized + debuginfo] target(s) in 0.66 secs
210");
211 }
212}