1use std::net::IpAddr;
9use std::path::Path;
10use std::fs::File;
11use std::io::{self, BufRead};
12use thiserror::Error;
13
14#[derive(Error, Debug)]
15pub enum RecordError {
16 #[error("Invalid Ipv4Addr: Must be private or global")]
17 InvalidIpAddress(String),
18}
19
20#[derive(Clone, Debug, Eq, PartialEq)]
22pub struct Record {
23 addr: IpAddr,
25 names: Vec<String>,
27}
28impl Record {
29 pub fn new(addr: IpAddr, names: Vec<String>) -> Result<Self, RecordError> {
30 if addr.is_ipv4() || addr.is_ipv6() {
33 return Ok(Self {
34 addr: addr,
35 names: names,
36 });
37 }
38
39 Err(RecordError::InvalidIpAddress(addr.to_string()))
40 }
41}
42
43#[derive(Error, Debug)]
44pub enum ParserError {
45 #[error(transparent)]
46 CouldNotOpen(#[from] std::io::Error),
47
48 #[error("bad ipv4 addr, not global or loopback")]
49 ParseError(#[from] std::net::AddrParseError),
50
51 #[error("unknown")]
52 Unknown(String),
53}
54
55#[derive(Debug, Default)]
56pub enum Part {
57 Addr,
58 Names,
59 Comment,
60 #[default]
61 Unknown,
62}
63
64#[derive(Debug)]
66struct Parser {
67 line: i64,
68 part: Part,
69 records: Vec<Record>
70}
71
72impl Default for Parser {
73 fn default() -> Parser {
74 let records: Vec<Record> = Vec::new();
75 Parser {line: 0, part: Part::Unknown, records: records}
76 }
77}
78
79impl Parser {
80 pub fn parse(&mut self, file: &Path) -> Result<&Vec<Record>, ParserError> {
81 let file = File::open(file)?;
82 let buff = io::BufReader::new(file).lines();
83
84 self.part = Part::Names;
85
86 for line in buff {
87 if let Ok(a) = line {
88 if a.is_empty() { continue; }
89 if a.starts_with('#') { continue; }
90 let a = a.replace("\t", " ");
92
93 let mut record_info =
94 a.split(' ').filter(|&s| !s.is_empty()).collect::<Vec<&str>>();
95
96 let name = record_info.remove(0).to_string();
97
98 let addrs =
99 record_info.iter().map(|&s| s.to_string()).collect::<Vec<String>>();
100
101 if let Ok(record) = Record::new(name.parse()?, addrs) {
102 self.records.push(record);
103 };
104 }
105 }
106
107 Ok(&self.records)
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114
115 #[test]
116 fn create_loopback() {
117 let addr = "127.0.0.1".parse().unwrap();
118 let names: Vec<String> = vec!["localhost".to_string()];
119 let record = Record::new(addr, names);
120 assert!(record.is_ok())
121 }
122
123 #[test]
124 fn create_private() {
125 let addr = "192.168.10.42".parse().unwrap();
126 let names: Vec<String> = vec!["core.naus".to_string()];
127 let record = Record::new(addr, names);
128 assert!(record.is_ok())
129 }
130
131 #[test]
132 fn test_parser() {
133 use std::path::Path;
134 let mut parser: Parser = Default::default();
135 let path = Path::new("/etc/hosts");
136 match parser.parse(path) {
137 Ok(_v) => println!("good to go"),
138 Err(e) => println!("{e:?}"),
139 }
140 }
141}