cdns-rs 1.2.2

A native Sync/Async Rust implementation of client DNS resolver.
Documentation
/*-
 * cdns-rs - a simple sync/async DNS query library
 * 
 * Copyright (C) 2020  Aleksandr Morozov
 * 
 * Copyright (C) 2025 Aleksandr Morozov
 * 
 * The syslog-rs crate can be redistributed and/or modified
 * under the terms of either of the following licenses:
 *
 *   1. the Mozilla Public License Version 2.0 (the “MPL”) OR
 *                     
 *   2. EUROPEAN UNION PUBLIC LICENCE v. 1.2 EUPL © the European Union 2007, 2016
 */

/// This file contains code of the simple tokenizer.

use std::iter::Peekable;
use std::str::Chars;

use crate::{internal_error_map, error::*};

pub(crate) struct ConfTokenizer<'token>
{
    srcmsg: &'token str,
    chars: Peekable<Chars<'token>>,
    pos: usize,
    curchar: Option<char>,
}

impl<'token> ConfTokenizer<'token>
{
    pub fn from_str(text_buf: &'token str) -> CDnsResult<Self>
    {
        let mut chars: Peekable<Chars> = text_buf.chars().peekable();
        
        let cur: Option<char> = 
            Some(chars.next().ok_or_else(|| internal_error_map!(CDnsErrorType::InternalError, "unexpected EOF"))?); 


        return Ok(
            Self
            {
                srcmsg: text_buf,
                chars: chars,
                pos: 0,
                curchar: cur,
            }
        );
    }

    #[inline] 
    fn move_next(&mut self)
    {
        self.pos += 1;

        self.curchar = self.chars.next();
    }

    #[inline]
    fn get_cur_char(&self) -> Option<char>
    {
        return self.curchar;
    }

    #[inline]
    fn foresee_char(&mut self) -> Option<char>
    {
        return match self.chars.peek()
        {
            Some(c) => Some(*c),
            None => None
        };
    }

    #[inline]
    fn is_eof(&mut self) -> bool
    {
        return self.curchar.is_none();
    }

    pub fn skip_upto_eol(&mut self)
    {
        while let Some(c) = self.get_cur_char()
        {
            if c == '\n'
            {
                self.move_next();
                break; // break from while
            }

            self.move_next();
        }
    }

    /// Reads from current position to EOL which is indocated by \n.
    /// Skips whitespaces.
    pub 
    fn read_upto_eol(&mut self) -> CDnsResult<Vec<String>>
    {
        let mut params: Vec<String> = Vec::new();

        while let Some(c) = self.get_cur_char()
        {
            if c == '\n'
            {
                self.move_next();
                break; // break from while
            }
            else if c.is_whitespace() == true
            {
                // skip white space
                self.move_next();
                continue;
            }

            let initpos = self.pos;

            loop
            {
                match self.get_cur_char()
                {
                    None => break,
                    Some(ref c) if c.is_whitespace() == true =>
                    {
                        break;
                    },
                    Some(ref c) if c.is_control() == true =>
                    {
                        self.move_next();
                        break;
                    },
                    Some(_c) => 
                    {
                        self.move_next();
                    }
                }
            }

            params.push(self.srcmsg[initpos..self.pos].to_string());
            
        }

        return Ok(params);
    }

    /// Skips all spaces and comments i.e # and reads until whitespace or EOF
    pub 
    fn read_next(&mut self) -> CDnsResult<Option<&str>>
    {
        loop
        {
            // skip spaces
            while self.is_eof() == false && self.get_cur_char().unwrap().is_whitespace() == true
            {
                self.move_next();
            }

            if self.is_eof() == true
            {
                return Ok(None);
            }

            if self.get_cur_char().unwrap() == '#'
            {
                self.move_next();

                while let Some(c) = self.get_cur_char()
                {
                    if c == '\n'
                    {
                        self.move_next();
                        break; // break from while
                    }
                    
                    self.move_next();
                }
            }
            else
            {
                // break from loop
                break;
            }
        }

        let initpos = self.pos;

        loop
        {
            match self.get_cur_char()
            {
                None => break,
                Some(ref c) if c.is_whitespace() == true =>
                {
                    break;
                },
                Some(ref c) if c.is_control() == true =>
                {
                    self.move_next();
                    break;
                },
                Some(_c) => 
                {
                    self.move_next();
                }
            }
        }

        let ret = &self.srcmsg[initpos..self.pos];

        return Ok(Some(ret));
    }
}