cdns_rs/
tokenizer.rs

1/*-
2 * cdns-rs - a simple sync/async DNS query library
3 * 
4 * Copyright (C) 2020  Aleksandr Morozov
5 * 
6 * Copyright (C) 2025 Aleksandr Morozov
7 * 
8 * The syslog-rs crate can be redistributed and/or modified
9 * under the terms of either of the following licenses:
10 *
11 *   1. the Mozilla Public License Version 2.0 (the “MPL”) OR
12 *                     
13 *   2. EUROPEAN UNION PUBLIC LICENCE v. 1.2 EUPL © the European Union 2007, 2016
14 */
15
16/// This file contains code of the simple tokenizer.
17
18use std::iter::Peekable;
19use std::str::Chars;
20
21use crate::{internal_error_map, error::*};
22
23pub(crate) struct ConfTokenizer<'token>
24{
25    srcmsg: &'token str,
26    chars: Peekable<Chars<'token>>,
27    pos: usize,
28    curchar: Option<char>,
29}
30
31impl<'token> ConfTokenizer<'token>
32{
33    pub fn from_str(text_buf: &'token str) -> CDnsResult<Self>
34    {
35        let mut chars: Peekable<Chars> = text_buf.chars().peekable();
36        
37        let cur: Option<char> = 
38            Some(chars.next().ok_or_else(|| internal_error_map!(CDnsErrorType::InternalError, "unexpected EOF"))?); 
39
40
41        return Ok(
42            Self
43            {
44                srcmsg: text_buf,
45                chars: chars,
46                pos: 0,
47                curchar: cur,
48            }
49        );
50    }
51
52    #[inline] 
53    fn move_next(&mut self)
54    {
55        self.pos += 1;
56
57        self.curchar = self.chars.next();
58    }
59
60    #[inline]
61    fn get_cur_char(&self) -> Option<char>
62    {
63        return self.curchar;
64    }
65
66    #[inline]
67    fn foresee_char(&mut self) -> Option<char>
68    {
69        return match self.chars.peek()
70        {
71            Some(c) => Some(*c),
72            None => None
73        };
74    }
75
76    #[inline]
77    fn is_eof(&mut self) -> bool
78    {
79        return self.curchar.is_none();
80    }
81
82    pub fn skip_upto_eol(&mut self)
83    {
84        while let Some(c) = self.get_cur_char()
85        {
86            if c == '\n'
87            {
88                self.move_next();
89                break; // break from while
90            }
91
92            self.move_next();
93        }
94    }
95
96    /// Reads from current position to EOL which is indocated by \n.
97    /// Skips whitespaces.
98    pub 
99    fn read_upto_eol(&mut self) -> CDnsResult<Vec<String>>
100    {
101        let mut params: Vec<String> = Vec::new();
102
103        while let Some(c) = self.get_cur_char()
104        {
105            if c == '\n'
106            {
107                self.move_next();
108                break; // break from while
109            }
110            else if c.is_whitespace() == true
111            {
112                // skip white space
113                self.move_next();
114                continue;
115            }
116
117            let initpos = self.pos;
118
119            loop
120            {
121                match self.get_cur_char()
122                {
123                    None => break,
124                    Some(ref c) if c.is_whitespace() == true =>
125                    {
126                        break;
127                    },
128                    Some(ref c) if c.is_control() == true =>
129                    {
130                        self.move_next();
131                        break;
132                    },
133                    Some(_c) => 
134                    {
135                        self.move_next();
136                    }
137                }
138            }
139
140            params.push(self.srcmsg[initpos..self.pos].to_string());
141            
142        }
143
144        return Ok(params);
145    }
146
147    /// Skips all spaces and comments i.e # and reads until whitespace or EOF
148    pub 
149    fn read_next(&mut self) -> CDnsResult<Option<&str>>
150    {
151        loop
152        {
153            // skip spaces
154            while self.is_eof() == false && self.get_cur_char().unwrap().is_whitespace() == true
155            {
156                self.move_next();
157            }
158
159            if self.is_eof() == true
160            {
161                return Ok(None);
162            }
163
164            if self.get_cur_char().unwrap() == '#'
165            {
166                self.move_next();
167
168                while let Some(c) = self.get_cur_char()
169                {
170                    if c == '\n'
171                    {
172                        self.move_next();
173                        break; // break from while
174                    }
175                    
176                    self.move_next();
177                }
178            }
179            else
180            {
181                // break from loop
182                break;
183            }
184        }
185
186        let initpos = self.pos;
187
188        loop
189        {
190            match self.get_cur_char()
191            {
192                None => break,
193                Some(ref c) if c.is_whitespace() == true =>
194                {
195                    break;
196                },
197                Some(ref c) if c.is_control() == true =>
198                {
199                    self.move_next();
200                    break;
201                },
202                Some(_c) => 
203                {
204                    self.move_next();
205                }
206            }
207        }
208
209        let ret = &self.srcmsg[initpos..self.pos];
210
211        return Ok(Some(ret));
212    }
213}