mod database;
pub use database::{GeoIpDb, GeoInfo, DbStats};
use std::net::{Ipv4Addr, Ipv6Addr};
#[derive(Debug, Clone, PartialEq)]
pub struct IpRange {
pub start_v4: Option<Ipv4Addr>,
pub start_v6: Option<Ipv6Addr>,
pub count: u128,
pub country: String,
}
pub fn parse_ripe_delegated(content: &str) -> Vec<IpRange> {
content
.lines()
.filter(|line| {
!line.starts_with('#')
&& !line.starts_with('2')
&& (line.contains("ipv4") || line.contains("ipv6"))
})
.filter_map(|line| {
let parts: Vec<&str> = line.split('|').collect();
if parts.len() < 7 {
return None;
}
let ip_type = parts[2];
let country = parts[1].to_string();
if ip_type == "ipv4" {
Some(IpRange {
start_v4: parts[3].parse().ok(),
start_v6: None,
count: parts[4].parse::<u32>().ok()? as u128,
country,
})
} else if ip_type == "ipv6" {
let prefix_len: u32 = parts[4].parse().ok()?;
let host_bits = 128 - prefix_len;
let count = if host_bits >= 128 {
u128::MAX
} else {
1u128 << host_bits
};
Some(IpRange {
start_v4: None,
start_v6: parts[3].parse().ok(),
count,
country,
})
} else {
None
}
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
use std::net::IpAddr;
#[test]
fn test_basic_usage() {
let db = GeoIpDb::new();
println!("\n Testing basic library usage:");
let ipv4: IpAddr = "46.4.0.1".parse().unwrap();
if let Some(info) = db.lookup(ipv4) {
println!(" 46.4.0.1 -> {} (EU: {})", info.country_code_str(), info.is_eu);
assert!(info.is_eu);
}
let is_eu = db.is_eu("46.4.0.1".parse().unwrap());
println!(" is_eu(46.4.0.1) = {}", is_eu);
let stats = db.stats();
println!(" Database: {} IPv4 ranges ({} EU, {} non-EU)",
stats.total_v4_ranges, stats.eu_v4_ranges, stats.non_eu_v4_ranges);
println!(" {} IPv6 ranges ({} EU, {} non-EU)",
stats.total_v6_ranges, stats.eu_v6_ranges, stats.non_eu_v6_ranges);
}
#[test]
fn test_ipv6_lookup() {
let db = GeoIpDb::new();
let ipv6: IpAddr = "2a01:4f8::1".parse().unwrap();
if let Some(info) = db.lookup(ipv6) {
println!(" 2a01:4f8::1 -> {} (EU: {})", info.country_code_str(), info.is_eu);
} else {
println!(" 2a01:4f8::1 not found in database");
}
}
}