prettytable/
utils.rs

1//! Internal only utilities
2use std::io::{Error, ErrorKind, Write};
3use std::str;
4
5use unicode_width::UnicodeWidthStr;
6
7use super::format::Alignment;
8
9#[cfg(any(not(windows), not(feature="win_crlf")))]
10pub static NEWLINE: &'static [u8] = b"\n";
11#[cfg(all(windows, feature="win_crlf"))]
12pub static NEWLINE: &'static [u8] = b"\r\n";
13
14/// Internal utility for writing data into a string
15pub struct StringWriter {
16    string: String,
17}
18
19impl StringWriter {
20    /// Create a new `StringWriter`
21    pub fn new() -> StringWriter {
22        StringWriter { string: String::new() }
23    }
24
25    /// Return a reference to the internally written `String`
26    pub fn as_string(&self) -> &str {
27        &self.string
28    }
29}
30
31impl Write for StringWriter {
32    fn write(&mut self, data: &[u8]) -> Result<usize, Error> {
33        let string = match str::from_utf8(data) {
34            Ok(s) => s,
35            Err(e) => {
36                return Err(Error::new(ErrorKind::Other,
37                                      format!("Cannot decode utf8 string : {}", e)))
38            }
39        };
40        self.string.push_str(string);
41        Ok(data.len())
42    }
43
44    fn flush(&mut self) -> Result<(), Error> {
45        // Nothing to do here
46        Ok(())
47    }
48}
49
50/// Align/fill a string and print it to `out`
51/// If `skip_right_fill` is set to `true`, then no space will be added after the string
52/// to complete alignment
53pub fn print_align<T: Write + ?Sized>(out: &mut T,
54                                      align: Alignment,
55                                      text: &str,
56                                      fill: char,
57                                      size: usize,
58                                      skip_right_fill: bool)
59                                      -> Result<(), Error> {
60    let text_len = display_width(text);
61    let mut nfill = if text_len < size { size - text_len } else { 0 };
62    let n = match align {
63        Alignment::LEFT => 0,
64        Alignment::RIGHT => nfill,
65        Alignment::CENTER => nfill / 2,
66    };
67    if n > 0 {
68        out.write_all(&vec![fill as u8; n])?;
69        nfill -= n;
70    }
71    out.write_all(text.as_bytes())?;
72    if nfill > 0 && !skip_right_fill {
73        out.write_all(&vec![fill as u8; nfill])?;
74    }
75    Ok(())
76}
77
78/// Return the display width of a unicode string.
79/// This functions takes ANSI-escaped color codes into account.
80pub fn display_width(text: &str) -> usize {
81    let width = UnicodeWidthStr::width(text);
82    let mut state = 0;
83    let mut hidden = 0;
84
85    for c in text.chars() {
86        state = match (state, c) {
87            (0, '\u{1b}') => 1,
88            (1, '[') => 2,
89            (1, _) => 0,
90            (2, 'm') => 3,
91            _ => state,
92        };
93
94        // We don't count escape characters as hidden as
95        // UnicodeWidthStr::width already considers them.
96        if state > 1 {
97            hidden += 1;
98        }
99
100        if state == 3 {
101            state = 0;
102        }
103    }
104
105    width - hidden
106}
107
108#[cfg(test)]
109mod tests {
110    use super::*;
111    use format::Alignment;
112    use std::io::Write;
113
114    #[test]
115    fn string_writer() {
116        let mut out = StringWriter::new();
117        out.write("foo".as_bytes()).unwrap();
118        out.write(" ".as_bytes()).unwrap();
119        out.write("".as_bytes()).unwrap();
120        out.write("bar".as_bytes()).unwrap();
121        assert_eq!(out.as_string(), "foo bar");
122    }
123
124    #[test]
125    fn fill_align() {
126        let mut out = StringWriter::new();
127        print_align(&mut out, Alignment::RIGHT, "foo", '*', 10, false).unwrap();
128        assert_eq!(out.as_string(), "*******foo");
129
130        let mut out = StringWriter::new();
131        print_align(&mut out, Alignment::LEFT, "foo", '*', 10, false).unwrap();
132        assert_eq!(out.as_string(), "foo*******");
133
134        let mut out = StringWriter::new();
135        print_align(&mut out, Alignment::CENTER, "foo", '*', 10, false).unwrap();
136        assert_eq!(out.as_string(), "***foo****");
137
138        let mut out = StringWriter::new();
139        print_align(&mut out, Alignment::CENTER, "foo", '*', 1, false).unwrap();
140        assert_eq!(out.as_string(), "foo");
141    }
142
143    #[test]
144    fn skip_right_fill() {
145        let mut out = StringWriter::new();
146        print_align(&mut out, Alignment::RIGHT, "foo", '*', 10, true).unwrap();
147        assert_eq!(out.as_string(), "*******foo");
148
149        let mut out = StringWriter::new();
150        print_align(&mut out, Alignment::LEFT, "foo", '*', 10, true).unwrap();
151        assert_eq!(out.as_string(), "foo");
152
153        let mut out = StringWriter::new();
154        print_align(&mut out, Alignment::CENTER, "foo", '*', 10, true).unwrap();
155        assert_eq!(out.as_string(), "***foo");
156
157        let mut out = StringWriter::new();
158        print_align(&mut out, Alignment::CENTER, "foo", '*', 1, false).unwrap();
159        assert_eq!(out.as_string(), "foo");
160    }
161
162    #[test]
163    fn utf8_error() {
164        let mut out = StringWriter::new();
165        let res = out.write_all(&vec![0, 255]);
166        assert!(res.is_err());
167    }
168}