domain_core/master/
reader.rs

1
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        // The borrow checker doesn’t like a ref mut of self.scanner and a
64        // ref of self.last at the same time, unless created at the same
65        // time. Some shenenigans are necessary to get that done.
66        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//============ Test ==========================================================
127
128#[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