1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
//! `term` module for testing buffered items against
//!
//! 'term' will create a object that will take in a 'buffer::TermWriter' and compare it against the current terminal properties.
//!
//! # Internal Example
//! ```
//! use console_tester::term::TermStrings;
//! let term_strings: TermStrings = TermStrings::new_from_env();
//! let term_list = term_strings.get_term_list();
//! ```

use std::path::{Path, PathBuf};
use terminfo::{capability, names, Database};

#[derive(Debug, Clone)]
pub struct TermStrings {
    /// Filtered list of terminal symbols
    string_list: Vec<Vec<u8>>,
}

/// TermStrings struct
/// Instantiable, in order to reduce performance overhead by caching the list of strings
/// instead of generating a new list for every check.
/// Populates TermStrings.string_list on instantiation
impl TermStrings {
    fn new(string_list: Option<Vec<Vec<u8>>>) -> TermStrings {
        match string_list {
            None => TermStrings {
                string_list: Vec::new(),
            },
            Some(string_list) => TermStrings {
                string_list: string_list,
            },
        }
    }
    pub fn new_from_env() -> TermStrings {
        TermStrings::new(init_from_env())
    }
    pub fn new_from_path(path: &Path) -> TermStrings {
        TermStrings::new(init_from_path(&path.to_owned()))
    }

    /// Check a terminal symbol (in Vec<u8> form) against the list of valid terminal symbols
    pub fn check_valid_symbol(&self, to_compare: Vec<u8>) -> bool {
        self.string_list.contains(&to_compare)
    }

    /// Get the stored terminal symbol list
    pub fn get_term_list(self) -> Vec<Vec<u8>> {
        self.string_list
    }
}

/// Gets a Vec of u8 vectors, each containing a terminal symbol
/// !Internal Function
///
/// Warning, printing these symbols to the terminal may result in strange side effects
fn init_from_env() -> Option<Vec<Vec<u8>>> {
    let res = Database::from_env();

    let info: Database;

    // if the terminal isn't supported essentially
    if res.is_err() {
        println!("This terminal isn't supported by the testing framework");
        return None;
    }

    // get database object now
    info = res.unwrap();

    let mut strings = Vec::new();

    for n in names::ALIASES.keys() {
        if let Some(val) = info.raw(n) {
            match &val {
                // We're only interested in the strings, so filter those out
                capability::Value::String(s) => strings.push(s.to_owned()),
                capability::Value::Number(_) => (),
                capability::Value::True => (),
            }
        }
    }
    Some(strings)
}

/// Gets a Vec of u8 vectors, each containing a terminal symbol.
/// This method takes a filepath to a terminfo file
/// Warning, printing these symbols to the terminal may result in strange side effects
fn init_from_path(path: &PathBuf) -> Option<Vec<Vec<u8>>> {
    let res = Database::from_path(path);

    let info: Database;

    // File not found, or invalid terminfo file
    if res.is_err() {
        println!("This terminal isn't supported by the testing framework");
        return None;
    }

    // get database object now
    info = res.unwrap();

    let mut strings = Vec::new();

    for n in names::ALIASES.keys() {
        if let Some(val) = info.raw(n) {
            match &val {
                // We're only interested in the strings, so filter those out
                capability::Value::String(s) => strings.push(s.to_owned()),
                capability::Value::Number(_) => (),
                capability::Value::True => (),
            }
        }
    }
    Some(strings)
}

// -------------------- TESTS -----------------------

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn term_struct_not_empty_from_terminfo_file() {
        let path = Path::new("./terminfo_files/x/xterm");
        let t = TermStrings::new_from_path(path);
        // println!("{:?}", t.string_list);
        assert!(!t.get_term_list().is_empty());
    }

    #[test]
    fn check_list_for_valid_symbol() {
        let path = Path::new("./terminfo_files/x/xterm");
        let t = TermStrings::new_from_path(path);
        assert!(t.check_valid_symbol([27, 91, 80].to_vec()));
    }

    #[test]
    fn check_list_for_invalid_symbol() {
        let t = TermStrings::new_from_env();
        assert!(!t.check_valid_symbol([27, 27, 27].to_vec()));
    }

    #[test]
    #[ignore]
    fn term_strings_init_from_env_not_empty() {
        let strings = init_from_env().unwrap();
        assert!(!strings.is_empty());
    }

    #[test]
    fn generate_termstrings_from_existing_list() {
        let path = Path::new("./terminfo_files/x/xterm");
        let t1 = TermStrings::new_from_path(path);
        let t2 = TermStrings::new(Some(t1.get_term_list()));
        assert!(!t2.get_term_list().is_empty());
    }
}