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}