wireless_regdb/
lexer.rs

1//! Lexer to create a TokenStream(`Vec<lexer::TokType>`) from a string represeting the database in txt format
2
3use anyhow::{bail, Result};
4
5/// Token Representing content from the txt file format of the binary database
6#[derive(Debug, PartialEq, Eq, Clone)]
7pub enum TokType {
8    /// String Token
9    String(String),
10    /// Integer represented as usize
11    Int(usize),
12    Colon,  // :
13    Equals, // =
14    Minus,  // -
15    Comma,  // ,
16    LParen, // (
17    RParen, // )
18    /// Single `@` Token
19    At, // @
20}
21
22impl TokType {
23    /// Parse a file from filesystem to a Vec of Tokens
24    ///
25    /// # Arguments
26    ///
27    /// * `path` - path to db.txt on disk
28    pub fn parse<P: AsRef<std::path::Path>>(file: P) -> Result<Vec<Self>> {
29        let db = std::fs::read_to_string(file)?;
30
31        Self::parse_str(&db)
32    }
33
34    /// Parse a string to a Vec of Tokens
35    ///
36    /// # Arguments
37    ///
38    /// * `db` - string of the database
39    ///
40    /// # Example
41    /// ```
42    /// use wireless_regdb::TokType;
43    /// let tokens = TokType::parse_str("wmmrule: ETSI").unwrap();
44    /// ```
45    pub fn parse_str(db: &str) -> Result<Vec<Self>> {
46        let mut result = Vec::new();
47
48        let mut it = db.chars().peekable();
49
50        while let Some(&c) = it.peek() {
51            match c {
52                ' ' => {
53                    it.next();
54                }
55                '\n' => {
56                    it.next();
57                }
58                '\t' => {
59                    it.next();
60                }
61                ':' => {
62                    it.next();
63                    result.push(TokType::Colon);
64                }
65                '=' => {
66                    it.next();
67                    result.push(TokType::Equals);
68                }
69                '-' => {
70                    it.next();
71                    result.push(TokType::Minus);
72                }
73                ',' => {
74                    it.next();
75                    result.push(TokType::Comma);
76                }
77                '(' => {
78                    it.next();
79                    result.push(TokType::LParen);
80                }
81                ')' => {
82                    it.next();
83                    result.push(TokType::RParen);
84                }
85                '@' => {
86                    it.next();
87                    result.push(TokType::At);
88                }
89                '#' => {
90                    while let Some(c) = it.next() {
91                        if c == '\n' {
92                            break;
93                        }
94                    }
95                }
96                _ => {
97                    let mut ret = String::new();
98                    while let Some(&c) = it.peek() {
99                        if c == '='
100                            || c == '\n'
101                            || c == ' '
102                            || c == ':'
103                            || c == ','
104                            || c == '('
105                            || c == ')'
106                        {
107                            break;
108                        }
109                        ret.push(c);
110                        it.next();
111                    }
112
113                    if ret.chars().all(char::is_numeric) {
114                        let ret = ret.parse::<usize>().unwrap(); // tested before
115                        result.push(TokType::Int(ret));
116                    } else {
117                        result.push(TokType::String(ret));
118                    }
119                }
120            }
121        }
122
123        Ok(result)
124    }
125
126    /// Return the string of the token, if the token contains a string
127    ///
128    /// # Example
129    /// ```
130    /// use wireless_regdb::TokType;
131    /// let token = TokType::String("hello_world".to_string());
132    /// let content = token.get_string().unwrap();
133    /// ```
134    pub fn get_string(&self) -> Result<String> {
135        match &self {
136            TokType::String(ret) => Ok(ret.to_string()),
137            v => bail!("token is not a string: {:?}", v),
138        }
139    }
140    /// Return the usize of the token, if the token contains an integer
141    ///
142    /// # Example
143    /// ```
144    /// use wireless_regdb::TokType;
145    /// let token = TokType::Int(1337);
146    /// let content = token.get_int().unwrap();
147    /// ```
148    pub fn get_int(&self) -> Result<usize> {
149        match &self {
150            TokType::Int(ret) => Ok(*ret),
151            v => bail!("token is not an int: {:?}", v),
152        }
153    }
154}
155
156#[cfg(test)]
157mod test {
158    use super::TokType;
159
160    #[test]
161    fn parse_wmmrule() {
162        #[rustfmt::skip]
163		let wmmrule = r#"
164wmmrule ETSI:
165  vo_c: cw_min=3, cw_max=7, aifsn=2, cot=2
166		"#;
167
168        let lexer = TokType::parse_str(wmmrule).unwrap();
169
170        // Colon, String("cw_min"), Equals, Int(3), Comma, String("cw_max"), Equals, Int(7), Comma, String("aifsn"), Equals, Int(2), Comma, String("cot"), Equals, Int(2)]
171        let should = vec![
172            TokType::String("wmmrule".to_string()),
173            TokType::String("ETSI".to_string()),
174            TokType::Colon,
175            TokType::String("vo_c".to_string()),
176            TokType::Colon,
177            TokType::String("cw_min".to_string()),
178            TokType::Equals,
179            TokType::Int(3),
180            TokType::Comma,
181            TokType::String("cw_max".to_string()),
182            TokType::Equals,
183            TokType::Int(7),
184            TokType::Comma,
185            TokType::String("aifsn".to_string()),
186            TokType::Equals,
187            TokType::Int(2),
188            TokType::Comma,
189            TokType::String("cot".to_string()),
190            TokType::Equals,
191            TokType::Int(2),
192        ];
193
194        assert_eq!(lexer, should);
195    }
196
197    #[test]
198    fn get_int() {
199        assert_eq!(TokType::Int(2).get_int().unwrap(), 2);
200
201        assert!(TokType::Comma.get_int().is_err());
202    }
203
204    #[test]
205    fn get_string() {
206        assert_eq!(
207            TokType::String("test".to_string()).get_string().unwrap(),
208            "test".to_string()
209        );
210
211        assert!(TokType::Comma.get_string().is_err());
212    }
213}