1#![deny(missing_docs)]
2
3use std::convert::TryFrom;
8use std::error::Error;
9use std::io::BufRead;
10use std::io::BufReader;
11use std::io::Read;
12
13#[cfg(feature = "download")]
14pub mod download;
15
16#[cfg(feature = "download")]
17pub use crate::download::*;
18
19#[derive(Debug, Clone)]
21pub enum Line {
22 Version(Version),
24
25 Summary(Summary),
27
28 Record(Record),
30}
31
32#[derive(Debug, Clone)]
34pub enum Type {
35 ASN,
37
38 IPv4,
40
41 IPv6,
43
44 Unknown,
46}
47
48impl TryFrom<&str> for Type {
50 type Error = &'static str;
51
52 fn try_from(value: &str) -> Result<Self, Self::Error> {
53 let string = value.to_lowercase();
54
55 if string.eq("asn") {
56 Ok(Type::ASN)
57 } else if string.eq("ipv4") {
58 Ok(Type::IPv4)
59 } else if string.eq("ipv6") {
60 Ok(Type::IPv6)
61 } else {
62 Err("Unknown type not matching 'asn', 'ipv4' or 'ipv6' found while attempting to parse resource type.")
63 }
64 }
65}
66
67#[derive(Debug, Clone)]
69pub struct Summary {
70 pub registry: String,
72
73 pub res_type: Type,
75
76 pub count: u32,
78}
79
80#[derive(Debug, Clone)]
82pub struct Version {
83 pub version: f64,
85
86 pub registry: String,
88
89 pub serial: String,
91
92 pub records: u32,
94
95 pub start_date: String,
97
98 pub end_date: String,
100
101 pub utc_offset: String,
103}
104
105#[derive(Debug, Clone)]
107pub struct Record {
108 pub registry: String,
110
111 pub organization: String,
113
114 pub res_type: Type,
116
117 pub start: String,
119
120 pub value: u32,
122
123 pub date: String,
125
126 pub status: String,
128
129 pub id: String,
131}
132
133pub fn read_all(read: impl Read) -> Result<impl Iterator<Item = Line>, Box<dyn Error>> {
137 let mut stream = BufReader::new(read);
138 let mut lines: Vec<Line> = Vec::new();
139
140 loop {
141 let mut line = String::new();
142 let len = stream.read_line(&mut line)?;
143
144 if len == 0 {
145 break;
146 }
147
148 line.pop();
150
151 if line.starts_with('#') {
153 continue;
154 }
155
156 let fields = line.split('|').collect::<Vec<_>>();
158
159 if fields[0].chars().all(|x| x.is_digit(10) || x.eq(&'.')) {
161 lines.push(Line::Version(Version {
162 version: fields[0].parse::<f64>().unwrap(),
163 registry: fields[1].to_string(),
164 serial: fields[2].to_string(),
165 records: fields[3].parse::<u32>().unwrap(),
166 start_date: fields[4].to_string(),
167 end_date: fields[5].to_string(),
168 utc_offset: fields[6].to_string(),
169 }));
170 continue;
171 }
172
173 if fields[5].to_string().eq("summary") {
175 lines.push(Line::Summary(Summary {
176 registry: fields[0].to_string(),
177 res_type: Type::try_from(fields[2])?,
178 count: fields[4].parse::<u32>().unwrap(),
179 }));
180 continue;
181 }
182
183 lines.push(Line::Record(Record {
184 registry: fields[0].to_string(),
185 organization: fields[1].to_string(),
186 res_type: Type::try_from(fields[2])?,
187 start: fields[3].to_string(),
188 value: fields[4].parse::<u32>().unwrap(),
189 date: fields[5].to_string(),
190 status: fields[6].to_string(),
191 id: if fields.len() > 7 {
192 fields[7].to_string()
193 } else {
194 "".to_string()
195 },
196 }));
197 }
198
199 Ok(lines.into_iter())
200}