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