use std::fs;
use std::io::Write;
use std::path::Path;
fn main() {
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=ripe-data.txt");
let ripe_content = fs::read_to_string("ripe-data.txt")
.expect("Failed to read ripe-data.txt - make sure it's in the project root");
let (v4_ranges, v6_ranges) = parse_ripe_data(&ripe_content);
println!("cargo:warning=Parsed {} IPv4 ranges from RIPE data", v4_ranges.len());
println!("cargo:warning=Parsed {} IPv6 ranges from RIPE data", v6_ranges.len());
let out_dir = std::env::var("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("generated_data.rs");
let mut file = fs::File::create(&dest_path).unwrap();
writeln!(file, "// Auto-generated from RIPE data at build time").unwrap();
writeln!(file, "// DO NOT EDIT - changes will be overwritten").unwrap();
writeln!(file, "").unwrap();
writeln!(
file,
"pub const IPV4_RANGES: &[(u32, u32, &str)] = &["
)
.unwrap();
for (start, count, country) in &v4_ranges {
if *count == 0 {
continue; }
let end = start.saturating_add(count.saturating_sub(1));
writeln!(file, " ({}, {}, \"{}\"),", start, end, country).unwrap();
}
writeln!(file, "];").unwrap();
writeln!(file, "").unwrap();
if v6_ranges.is_empty() {
writeln!(file, "pub const IPV6_RANGES: &[(u128, u128, &str)] = &[];").unwrap();
} else {
writeln!(
file,
"pub const IPV6_RANGES: &[(u128, u128, &str)] = &["
)
.unwrap();
for (start, end, country) in &v6_ranges {
writeln!(file, " ({}, {}, \"{}\"),", start, end, country).unwrap();
}
writeln!(file, "];").unwrap();
}
println!("cargo:warning=Generated data file with {} IPv4 ranges and {} IPv6 ranges",
v4_ranges.len(), v6_ranges.len());
}
fn parse_ripe_data(content: &str) -> (Vec<(u32, u32, String)>, Vec<(u128, u128, String)>) {
let mut v4_ranges = Vec::new();
let mut v6_ranges = Vec::new();
for line in content.lines() {
if line.starts_with('#') || line.starts_with('2') {
continue;
}
let parts: Vec<&str> = line.split('|').collect();
if parts.len() < 7 {
continue;
}
let country = parts[1].to_string();
let ip_type = parts[2];
let start_str = parts[3];
let count_str = parts[4];
if ip_type == "ipv4" {
if let Ok(start_ip) = start_str.parse::<std::net::Ipv4Addr>() {
if let Ok(count) = count_str.parse::<u32>() {
if count == 0 { continue; }
let start_u32: u32 = start_ip.into();
v4_ranges.push((start_u32, count, country));
}
}
} else if ip_type == "ipv6" {
if let Ok(start_ip) = start_str.parse::<std::net::Ipv6Addr>() {
if let Ok(prefix_len) = count_str.parse::<u32>() {
let start_u128: u128 = start_ip.into();
let host_bits = 128 - prefix_len;
let count = if host_bits >= 128 {
u128::MAX
} else {
1u128 << host_bits
};
let end = start_u128.saturating_add(count).saturating_sub(1);
v6_ranges.push((start_u128, end, country));
}
}
}
}
v4_ranges.sort_by_key(|r| r.0);
v6_ranges.sort_by_key(|r| r.0);
(v4_ranges, v6_ranges)
}