domain_core/master/
reader.rs1
2use std::fmt;
3use std::io;
4use std::path::{Path, PathBuf};
5use ::bits::name::Dname;
6use ::iana::Class;
7use super::entry::{Entry, MasterRecord};
8use super::scan::{CharSource, Pos, ScanError, Scanner};
9use super::source::Utf8File;
10
11
12pub struct Reader<C: CharSource> {
13 scanner: Option<Scanner<C>>,
14 ttl: Option<u32>,
15 last: Option<(Dname, Class)>,
16}
17
18impl<C: CharSource> Reader<C> {
19 pub fn new(source: C) -> Self {
20 Reader {
21 scanner: Some(Scanner::new(source)),
22 ttl: None,
23 last: None
24 }
25 }
26}
27
28impl Reader<Utf8File> {
29 pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, io::Error> {
30 Utf8File::open(path).map(Self::new)
31 }
32}
33
34impl<C: CharSource> Reader<C> {
35 #[allow(match_same_arms)]
36 pub fn next_record(&mut self) -> Result<Option<ReaderItem>, ScanError> {
37 loop {
38 match self.next_entry() {
39 Ok(Some(Entry::Origin(origin))) => self.set_origin(origin),
40 Ok(Some(Entry::Include{ path, origin })) => {
41 return Ok(Some(ReaderItem::Include { path, origin }))
42 }
43 Ok(Some(Entry::Ttl(ttl))) => self.ttl = Some(ttl),
44 Ok(Some(Entry::Control{ name, start })) => {
45 return Ok(Some(ReaderItem::Control { name, start }))
46 }
47 Ok(Some(Entry::Record(record))) => {
48 self.last = Some((record.owner().clone(),
49 record.class()));
50 return Ok(Some(ReaderItem::Record(record)))
51 }
52 Ok(Some(Entry::Blank)) => { }
53 Ok(None) => return Ok(None),
54 Err(err) => {
55 self.scanner = None;
56 return Err(err)
57 }
58 }
59 }
60 }
61
62 fn next_entry(&mut self) -> Result<Option<Entry>, ScanError> {
63 let (scanner, owner, class) = match (&mut self.scanner, &self.last) {
67 (&mut Some(ref mut scanner), &Some((ref owner, class))) => {
68 (scanner, Some(owner), Some(class))
69 }
70 (&mut Some(ref mut scanner), _) => {
71 (scanner, None, None)
72 }
73 (&mut None, _) => {
74 return Ok(None)
75 }
76 };
77 Entry::scan(scanner, owner, class, self.ttl)
78 }
79
80 fn set_origin(&mut self, origin: Dname) {
81 if let Some(ref mut scanner) = self.scanner {
82 scanner.set_origin(Some(origin))
83 }
84 }
85}
86
87impl<C: CharSource> Iterator for Reader<C> {
88 type Item = Result<ReaderItem, ScanError>;
89
90 fn next(&mut self) -> Option<Self::Item> {
91 match self.next_record() {
92 Ok(Some(res)) => Some(Ok(res)),
93 Ok(None) => None,
94 Err(err) => Some(Err(err))
95 }
96 }
97}
98
99
100#[derive(Clone, Debug)]
101pub enum ReaderItem {
102 Record(MasterRecord),
103 Include { path: PathBuf, origin: Option<Dname> },
104 Control { name: String, start: Pos },
105}
106
107impl fmt::Display for ReaderItem {
108 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
109 match *self {
110 ReaderItem::Record(ref record) => write!(f, "{}", record),
111 ReaderItem::Include { ref path, ref origin } => {
112 try!(write!(f, "$INCLUDE {}", path.display()));
113 if let Some(ref origin) = *origin {
114 try!(write!(f, " {}", origin));
115 }
116 Ok(())
117 }
118 ReaderItem::Control { ref name, .. } => {
119 write!(f, "{}", name)
120 }
121 }
122 }
123}
124
125
126#[cfg(test)]
129mod test {
130 use super::*;
131 use ::master::scan::ScanError;
132
133 #[test]
134 fn print() {
135 let reader = Reader::new(&"$ORIGIN ISI.EDU.
136$TTL 86400
137@ IN SOA VENERA Action\\.domains (
138 20 ; SERIAL
139 7200 ; REFRESH
140 600 ; RETRY
141 3600000; EXPIRE
142 60) ; MINIMUM
143
144 NS A.ISI.EDU.
145 NS VENERA
146 NS VAXA
147 MX 10 VENERA
148 MX 20 VAXA
149
150A A 26.3.0.103
151
152VENERA A 10.1.0.52
153 A 128.9.0.32
154
155VAXA A 10.2.0.27
156 A 128.9.0.33
157
158
159$INCLUDE <SUBSYS>ISI-MAILBOXES.TXT"[..]);
160
161 for item in reader {
162 match item {
163 Ok(item) => println!("{}", item),
164 Err(ScanError::Syntax(err, pos)) => {
165 panic!("{}:{}: {:?}", pos.line(), pos.col(), err);
166 }
167 Err(err) => panic!("{:?}", err)
168 }
169 }
170 }
171}
172