Rust tool to convert IPFire Location dump into CSV format.
[tor.git] / scripts / maint / geoip / geoip-db-tool / src / db.rs
blobeaadd4c61269f24e9f4d522d0763540268223c17
1 /// Code to parse a dump file
2 use std::collections::HashMap;
3 use std::convert::TryInto;
4 use std::iter::Peekable;
6 use super::NetBlock;
8 pub struct BlockReader<I>
9 where
10     I: Iterator<Item = std::io::Result<String>>,
12     iter: Peekable<I>,
15 enum AnyBlock {
16     NotNet,
17     NetBlock(NetBlock),
20 impl<I> BlockReader<I>
21 where
22     I: Iterator<Item = std::io::Result<String>>,
24     pub fn new(iter: I) -> Self {
25         BlockReader {
26             iter: iter.peekable(),
27         }
28     }
30     /// Extract the initial header from the file.
31     pub fn extract_header(&mut self) -> String {
32         let mut res: String = "".to_string();
34         while let Some(Ok(line)) = self.iter.peek() {
35             if !line.starts_with('#') {
36                 break;
37             }
38             res.push_str(line.as_str());
39             res.push('\n');
40             let _ = self.iter.next();
41         }
43         res
44     }
46     /// Extract the next empty-line-delimited block from the file.
47     ///
48     /// This isn't terribly efficient, but it's "fast enough".
49     fn get_block(&mut self) -> Option<std::io::Result<AnyBlock>> {
50         let mut kv = HashMap::new();
52         while let Some(line) = self.iter.next() {
53             //dbg!(&line);
54             if let Err(e) = line {
55                 return Some(Err(e));
56             }
57             let line_orig = line.unwrap();
58             let line = line_orig.splitn(2, '#').next().unwrap().trim();
59             if line.is_empty() {
60                 if kv.is_empty() {
61                     continue;
62                 } else {
63                     break;
64                 }
65             }
66             let kwds: Vec<_> = line.splitn(2, ':').collect();
67             if kwds.len() != 2 {
68                 return None; // XXXX handle the error better.
69             }
70             kv.insert(kwds[0].trim().to_string(), kwds[1].trim().to_string());
71         }
73         if kv.is_empty() {
74             return None;
75         }
77         let net = if let Some(net) = kv.get("net") {
78             net.parse().unwrap() //XXXX handle the error better.
79         } else {
80             return Some(Ok(AnyBlock::NotNet));
81         };
83         let cc = if let Some(country) = kv.get("country") {
84             assert!(country.as_bytes().len() == 2);
85             country.as_bytes()[0..2].try_into().unwrap()
86         } else {
87             return Some(Ok(AnyBlock::NotNet));
88         };
90         fn is_true(v: Option<&String>) -> bool {
91             match v {
92                 Some(s) => s == "true",
93                 None => false,
94             }
95         }
97         let is_anon_proxy = is_true(kv.get("is-anonymous-proxy"));
98         let is_anycast = is_true(kv.get("is-anycast-proxy"));
99         let is_satellite = is_true(kv.get("is-satellite-provider"));
101         Some(Ok(AnyBlock::NetBlock(NetBlock {
102             net,
103             cc,
104             is_anon_proxy,
105             is_anycast,
106             is_satellite,
107         })))
108     }
111 impl<I> Iterator for BlockReader<I>
112 where
113     I: Iterator<Item = std::io::Result<String>>,
115     type Item = NetBlock;
116     fn next(&mut self) -> Option<Self::Item> {
117         loop {
118             match self.get_block() {
119                 None => return None,
120                 Some(Err(_)) => return None,
121                 Some(Ok(AnyBlock::NotNet)) => continue,
122                 Some(Ok(AnyBlock::NetBlock(n))) => return Some(n),
123             }
124         }
125     }