1use crate::common::IpFamily;
2use chrono::{Datelike, Local, Timelike};
3use ipnet::IpNet;
4use std::{
5 collections::BTreeSet,
6 error::Error,
7 fs::{self, OpenOptions},
8 io::Write,
9 path::Path,
10};
11
12pub fn write_ip_lists_to_files(
15 country_code: &str,
16 ipv4_list: &BTreeSet<IpNet>,
17 ipv6_list: &BTreeSet<IpNet>,
18 mode: &str,
19) -> Result<(), Box<dyn Error + Send + Sync>> {
20 let ipv4_file = format!("IPv4_{}.txt", country_code);
21 let ipv6_file = format!("IPv6_{}.txt", country_code);
22
23 write_single_ip_list(&ipv4_file, ipv4_list, mode)?;
24 write_single_ip_list(&ipv6_file, ipv6_list, mode)?;
25
26 Ok(())
27}
28
29fn write_single_ip_list<P: AsRef<Path>>(
31 path: P,
32 nets: &BTreeSet<IpNet>,
33 mode: &str,
34) -> Result<(), Box<dyn Error + Send + Sync>> {
35 let now = Local::now();
36 let formatted_header = format!(
37 "# {}/{}/{} {}:{}\n",
38 now.year(),
39 now.month(),
40 now.day(),
41 now.hour(),
42 now.minute()
43 );
44
45 let lines: Vec<String> = nets.iter().map(|net| net.to_string()).collect();
46 let content = format!("{}{}\n", formatted_header, lines.join("\n"));
47
48 match mode {
49 "append" => {
50 let mut file = OpenOptions::new().create(true).append(true).open(&path)?;
51 file.write_all(content.as_bytes())?;
52 println!("[output] Appended IP list to: {}", path.as_ref().display());
53 }
54 _ => {
55 fs::write(&path, &content)?;
56 println!(
57 "[output] Wrote (overwrite) IP list to: {}",
58 path.as_ref().display()
59 );
60 }
61 }
62
63 Ok(())
64}
65
66pub fn write_as_ip_list_to_file(
69 as_number: &str,
70 family: IpFamily,
71 ipnets: &BTreeSet<IpNet>,
72 mode: &str,
73) -> Result<(), Box<dyn Error + Send + Sync>> {
74 let now = Local::now().format("%Y-%m-%d %H:%M").to_string();
75 let file_name = format!("AS_{}_{}.txt", as_number, family.as_str());
76
77 let header = format!("# Execution Date and Time: {}\n", now);
78 let body = ipnets
79 .iter()
80 .map(IpNet::to_string)
81 .collect::<Vec<_>>()
82 .join("\n");
83 let content = format!("{}{}\n", header, body);
84
85 match mode {
86 "append" => {
87 let mut file = OpenOptions::new()
88 .create(true)
89 .append(true)
90 .open(&file_name)?;
91 file.write_all(content.as_bytes())?;
92 println!("[output] Appended IP list to: {}", file_name);
93 }
94 _ => {
95 fs::write(&file_name, content)?;
96 println!("[output] Wrote (overwrite) IP list to: {}", file_name);
97 }
98 }
99
100 Ok(())
101}
102
103pub fn write_overlap_to_file(
106 country_code: &str,
107 as_number: &str,
108 overlaps: &BTreeSet<IpNet>,
109 mode: &str,
110) -> Result<(), Box<dyn Error + Send + Sync>> {
111 if overlaps.is_empty() {
112 println!(
113 "[overlap] No overlap found for country={} and AS={}",
114 country_code, as_number
115 );
116 return Ok(());
117 }
118
119 let filename = format!("overlap_{}_{}.txt", country_code, as_number);
120 let now_str = Local::now().format("%Y-%m-%d %H:%M").to_string();
121 let header = format!(
122 "# Overlap between Country={} and AS={} at {}\n",
123 country_code, as_number, now_str
124 );
125
126 let body = overlaps
127 .iter()
128 .map(|net| net.to_string())
129 .collect::<Vec<_>>()
130 .join("\n");
131
132 let content = format!("{}\n{}\n", header, body);
133
134 match mode {
135 "append" => {
136 let mut file = OpenOptions::new()
137 .create(true)
138 .append(true)
139 .open(&filename)?;
140 file.write_all(content.as_bytes())?;
141 println!("[overlap] Appended overlaps to: {}", filename);
142 }
143 _ => {
144 fs::write(&filename, content)?;
145 println!("[overlap] Wrote overlaps to: {}", filename);
146 }
147 }
148
149 Ok(())
150}