use std::path::PathBuf;
use ::bits::name::Dname;
use ::bits::record::Record;
use ::iana::{Class, Rtype};
use ::rdata::MasterRecordData;
use ::master::scan::{CharSource, Pos, Scan, ScanError, Scanner,
SyntaxError};
#[derive(Clone, Debug)]
pub enum Entry {
Origin(Dname),
Include { path: PathBuf, origin: Option<Dname> },
Ttl(u32),
Control { name: String, start: Pos },
Record(MasterRecord),
Blank
}
impl Entry {
pub fn scan<C: CharSource>(scanner: &mut Scanner<C>,
last_owner: Option<&Dname>,
last_class: Option<Class>,
default_ttl: Option<u32>)
-> Result<Option<Self>, ScanError> {
if scanner.is_eof() {
Ok(None)
}
else if let Ok(entry) = Self::scan_control(scanner) {
Ok(Some(entry))
}
else if let Ok(()) = Self::scan_blank(scanner) {
Ok(Some(Entry::Blank))
}
else {
let record = Self::scan_record(scanner, last_owner,
last_class, default_ttl)?;
Ok(Some(Entry::Record(record)))
}
}
fn scan_blank<C: CharSource>(scanner: &mut Scanner<C>)
-> Result<(), ScanError> {
scanner.scan_opt_space()?;
scanner.scan_newline()?;
Ok(())
}
fn scan_control<C: CharSource>(scanner: &mut Scanner<C>)
-> Result<Self, ScanError> {
match ControlType::scan(scanner)? {
ControlType::Origin => {
let name = Dname::scan(scanner)?;
scanner.scan_newline()?;
Ok(Entry::Origin(name))
}
ControlType::Include => {
let path = scanner.scan_string_phrase(|x| Ok(x.into()))?;
let origin = Dname::scan(scanner).ok();
scanner.scan_newline()?;
Ok(Entry::Include { path, origin })
}
ControlType::Ttl => {
let ttl = u32::scan(scanner)?;
scanner.scan_newline()?;
Ok(Entry::Ttl(ttl))
}
ControlType::Other(name, pos) => {
scanner.skip_entry()?;
Ok(Entry::Control { name, start: pos })
}
}
}
fn scan_record<C: CharSource>(scanner: &mut Scanner<C>,
last_owner: Option<&Dname>,
last_class: Option<Class>,
default_ttl: Option<u32>)
-> Result<MasterRecord, ScanError> {
let owner = Self::scan_owner(scanner, last_owner)?;
let (ttl, class) = Self::scan_ttl_class(scanner, last_class,
default_ttl)?;
let rtype = Rtype::scan(scanner)?;
let rdata = MasterRecordData::scan(rtype, scanner)?;
scanner.scan_newline()?;
Ok(Record::new(owner, class, ttl, rdata))
}
fn scan_owner<C: CharSource>(scanner: &mut Scanner<C>,
last_owner: Option<&Dname>)
-> Result<Dname, ScanError> {
let pos = scanner.pos();
if let Ok(()) = scanner.scan_space() {
if let Some(owner) = last_owner { Ok(owner.clone()) }
else { Err(ScanError::Syntax(SyntaxError::NoLastOwner, pos)) }
}
else if let Ok(()) = scanner.skip_literal("@") {
if let Some(ref origin) = *scanner.origin() { Ok(origin.clone()) }
else { Err(ScanError::Syntax(SyntaxError::NoOrigin, pos)) }
}
else {
Dname::scan(scanner)
}
}
fn scan_ttl_class<C: CharSource>(scanner: &mut Scanner<C>,
last_class: Option<Class>,
default_ttl: Option<u32>)
-> Result<(u32, Class), ScanError> {
let pos = scanner.pos();
let (ttl, class) = match u32::scan(scanner) {
Ok(ttl) => {
match Class::scan(scanner) {
Ok(class) => {
(Some(ttl), Some(class))
}
Err(_) => (Some(ttl), None)
}
}
Err(_) => {
match Class::scan(scanner) {
Ok(class) => {
match u32::scan(scanner) {
Ok(ttl) => {
(Some(ttl), Some(class))
}
Err(_) => (None, Some(class))
}
}
Err(_) => (None, None)
}
}
};
let ttl = match ttl.or(default_ttl) {
Some(ttl) => ttl,
None => {
return Err(ScanError::Syntax(SyntaxError::NoDefaultTtl, pos))
}
};
let class = match class.or(last_class) {
Some(class) => class,
None => {
return Err(ScanError::Syntax(SyntaxError::NoLastClass, pos))
}
};
Ok((ttl, class))
}
}
#[derive(Clone, Debug)]
enum ControlType {
Origin,
Include,
Ttl,
Other(String, Pos)
}
impl Scan for ControlType {
fn scan<C: CharSource>(scanner: &mut Scanner<C>)
-> Result<Self, ScanError> {
let pos = scanner.pos();
scanner.scan_string_word(|word| {
if word.eq_ignore_ascii_case("$ORIGIN") {
Ok(ControlType::Origin)
}
else if word.eq_ignore_ascii_case("$INCLUDE") {
Ok(ControlType::Include)
}
else if word.eq_ignore_ascii_case("$TTL") {
Ok(ControlType::Ttl)
}
else if let Some('$') = word.chars().next() {
Ok(ControlType::Other(word.to_owned(), pos))
}
else {
Err(SyntaxError::Expected(String::from("$")))
}
})
}
}
pub type MasterRecord = Record<Dname, MasterRecordData<Dname>>;