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 2025 Aleksandr Morozov
7 * 
8 * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by
9 * the European Commission - subsequent versions of the EUPL (the "Licence").
10 * 
11 * You may not use this work except in compliance with the Licence.
12 * 
13 * You may obtain a copy of the Licence at:
14 * 
15 *    https://joinup.ec.europa.eu/software/page/eupl
16 * 
17 * Unless required by applicable law or agreed to in writing, software
18 * distributed under the Licence is distributed on an "AS IS" basis, WITHOUT
19 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
20 * Licence for the specific language governing permissions and limitations
21 * under the Licence.
22 */
23
24/// This file contains code of the simple tokenizer.
25
26use std::iter::Peekable;
27use std::str::Chars;
28
29use crate::{internal_error_map, error::*};
30
31pub(crate) struct ConfTokenizer<'token>
32{
33    srcmsg: &'token str,
34    chars: Peekable<Chars<'token>>,
35    pos: usize,
36    curchar: Option<char>,
37}
38
39impl<'token> ConfTokenizer<'token>
40{
41    pub fn from_str(text_buf: &'token str) -> CDnsResult<Self>
42    {
43        let mut chars: Peekable<Chars> = text_buf.chars().peekable();
44        
45        let cur: Option<char> = 
46            Some(chars.next().ok_or_else(|| internal_error_map!(CDnsErrorType::InternalError, "unexpected EOF"))?); 
47
48
49        return Ok(
50            Self
51            {
52                srcmsg: text_buf,
53                chars: chars,
54                pos: 0,
55                curchar: cur,
56            }
57        );
58    }
59
60    #[inline] 
61    fn move_next(&mut self)
62    {
63        self.pos += 1;
64
65        self.curchar = self.chars.next();
66    }
67
68    #[inline]
69    fn get_cur_char(&self) -> Option<char>
70    {
71        return self.curchar;
72    }
73
74    #[inline]
75    fn foresee_char(&mut self) -> Option<char>
76    {
77        return match self.chars.peek()
78        {
79            Some(c) => Some(*c),
80            None => None
81        };
82    }
83
84    #[inline]
85    fn is_eof(&mut self) -> bool
86    {
87        return self.curchar.is_none();
88    }
89
90    pub fn skip_upto_eol(&mut self)
91    {
92        while let Some(c) = self.get_cur_char()
93        {
94            if c == '\n'
95            {
96                self.move_next();
97                break; // break from while
98            }
99
100            self.move_next();
101        }
102    }
103
104    /// Reads from current position to EOL which is indocated by \n.
105    /// Skips whitespaces.
106    pub 
107    fn read_upto_eol(&mut self) -> CDnsResult<Vec<String>>
108    {
109        let mut params: Vec<String> = Vec::new();
110
111        while let Some(c) = self.get_cur_char()
112        {
113            if c == '\n'
114            {
115                self.move_next();
116                break; // break from while
117            }
118            else if c.is_whitespace() == true
119            {
120                // skip white space
121                self.move_next();
122                continue;
123            }
124
125            let initpos = self.pos;
126
127            loop
128            {
129                match self.get_cur_char()
130                {
131                    None => break,
132                    Some(ref c) if c.is_whitespace() == true =>
133                    {
134                        break;
135                    },
136                    Some(ref c) if c.is_control() == true =>
137                    {
138                        self.move_next();
139                        break;
140                    },
141                    Some(_c) => 
142                    {
143                        self.move_next();
144                    }
145                }
146            }
147
148            params.push(self.srcmsg[initpos..self.pos].to_string());
149            
150        }
151
152        return Ok(params);
153    }
154
155    /// Skips all spaces and comments i.e # and reads until whitespace or EOF
156    pub 
157    fn read_next(&mut self) -> CDnsResult<Option<&str>>
158    {
159        loop
160        {
161            // skip spaces
162            while self.is_eof() == false && self.get_cur_char().unwrap().is_whitespace() == true
163            {
164                self.move_next();
165            }
166
167            if self.is_eof() == true
168            {
169                return Ok(None);
170            }
171
172            if self.get_cur_char().unwrap() == '#'
173            {
174                self.move_next();
175
176                while let Some(c) = self.get_cur_char()
177                {
178                    if c == '\n'
179                    {
180                        self.move_next();
181                        break; // break from while
182                    }
183                    
184                    self.move_next();
185                }
186            }
187            else
188            {
189                // break from loop
190                break;
191            }
192        }
193
194        let initpos = self.pos;
195
196        loop
197        {
198            match self.get_cur_char()
199            {
200                None => break,
201                Some(ref c) if c.is_whitespace() == true =>
202                {
203                    break;
204                },
205                Some(ref c) if c.is_control() == true =>
206                {
207                    self.move_next();
208                    break;
209                },
210                Some(_c) => 
211                {
212                    self.move_next();
213                }
214            }
215        }
216
217        let ret = &self.srcmsg[initpos..self.pos];
218
219        return Ok(Some(ret));
220    }
221}