use std::collections::BTreeMap;
use error::*;
use rr::{Name, IntoRecordSet, RecordType, Record, DNSClass, RData, RrKey, RecordSet};
use super::master_lex::{Lexer, Token};
pub struct Parser;
impl Parser {
pub fn new() -> Self {
Parser
}
pub fn parse(&mut self,
lexer: Lexer,
origin: Option<Name>)
-> ParseResult<(Name, BTreeMap<RrKey, RecordSet>)> {
let mut lexer = lexer;
let mut records: BTreeMap<RrKey, RecordSet> = BTreeMap::new();
let mut origin: Option<Name> = origin;
let mut current_name: Option<Name> = None;
let mut rtype: Option<RecordType> = None;
let mut ttl: Option<u32> = None;
let mut class: Option<DNSClass> = None;
let mut state = State::StartLine;
let mut tokens: Vec<Token> = Vec::new();
while let Some(t) = try!(lexer.next_token()) {
state = match state {
State::StartLine => {
rtype = None;
tokens.clear();
match t {
Token::Include => unimplemented!(),
Token::Origin => State::Origin,
Token::Ttl => State::Ttl,
Token::CharData(ref data) => {
current_name = Some(try!(Name::parse(data, origin.as_ref())));
State::TtlClassType
}
Token::At => {
current_name = origin.clone(); State::TtlClassType
}
Token::Blank => State::TtlClassType,
Token::EOL => State::StartLine, _ => return Err(ParseErrorKind::UnexpectedToken(t).into()),
}
}
State::Ttl => {
match t {
Token::CharData(ref data) => {
ttl = Some(try!(Self::parse_time(data)));
State::StartLine
}
_ => return Err(ParseErrorKind::UnexpectedToken(t).into()),
}
}
State::Origin => {
match t {
Token::CharData(ref data) => {
origin = Some(try!(Name::parse(data, None)));
State::StartLine
}
_ => return Err(ParseErrorKind::UnexpectedToken(t).into()),
}
}
State::Include => unimplemented!(),
State::TtlClassType => {
match t {
Token::CharData(ref data) => {
let result: ParseResult<u32> = Self::parse_time(data);
if result.is_ok() {
ttl = result.ok();
State::TtlClassType } else {
let result = DNSClass::from_str(data);
if result.is_ok() {
class = result.ok();
State::TtlClassType
} else {
rtype = Some(try!(RecordType::from_str(data)));
State::Record
}
}
}
Token::EOL => {
State::StartLine }
_ => return Err(ParseErrorKind::UnexpectedToken(t).into()),
}
}
State::Record => {
match t {
Token::EOL => {
let rdata = try!(RData::parse(try!(rtype.ok_or(ParseError::from(ParseErrorKind::Message("record type not specified")))), &tokens, origin.as_ref()));
let mut record = Record::new();
record.set_name(try!(current_name.clone().ok_or(ParseError::from(ParseErrorKind::Message("record name not specified")))));
record.set_rr_type(rtype.unwrap());
record.set_dns_class(try!(class.ok_or(ParseError::from(ParseErrorKind::Message("record class not specified")))));
match rtype.unwrap() {
RecordType::SOA => {
if let RData::SOA(ref soa) = rdata {
record.set_ttl(soa.expire() as u32); if ttl.is_none() {
ttl = Some(soa.minimum());
} } else {
assert!(false,
"Invalid RData here, expected SOA: {:?}",
rdata);
}
}
_ => {
record.set_ttl(try!(ttl.ok_or(ParseError::from(ParseErrorKind::Message("record ttl not specified")))));
}
}
record.set_rdata(rdata);
let key = RrKey::new(record.name(), record.rr_type());
match rtype.unwrap() {
RecordType::SOA => {
let set = record.into_record_set();
if records.insert(key, set).is_some() {
return Err(ParseErrorKind::Message("SOA is already \
specified")
.into());
}
}
_ => {
let mut set = records
.entry(key)
.or_insert(RecordSet::new(record.name(),
record.rr_type(),
0));
set.insert(record, 0);
}
}
State::StartLine
}
_ => {
tokens.push(t);
State::Record
}
}
}
}
}
let origin = try!(origin.ok_or(ParseError::from(ParseErrorKind::Message("$ORIGIN was not specified"))));
Ok((origin, records))
}
pub fn parse_time(ttl_str: &str) -> ParseResult<u32> {
let mut value: u32 = 0;
let mut collect: u32 = 0;
for c in ttl_str.chars() {
match c {
'0'...'9' => {
collect *= 10;
collect += try!(c.to_digit(10).ok_or(ParseErrorKind::CharToIntError(c)));
}
'S' | 's' => {
value += collect;
collect = 0;
}
'M' | 'm' => {
value += collect * 60;
collect = 0;
}
'H' | 'h' => {
value += collect * 3600;
collect = 0;
}
'D' | 'd' => {
value += collect * 86400;
collect = 0;
}
'W' | 'w' => {
value += collect * 604800;
collect = 0;
}
_ => return Err(ParseErrorKind::ParseTimeError(ttl_str.to_string()).into()),
}
}
return Ok(value + collect); }
}
#[allow(unused)]
enum State {
StartLine, TtlClassType, Ttl, Record,
Include, Origin,
}