Skip to main content

fire_scope/
output.rs

1use crate::common::{IpFamily, OutputFormat};
2use crate::error::AppError;
3use crate::output_common::{make_header, sanitize_identifier, write_list_nft, write_list_txt};
4use chrono::Local;
5use ipnet::IpNet;
6use std::collections::BTreeSet;
7use crate::common::debug_log;
8
9/// IPv4/IPv6リストをファイルに書き出す
10/// 国コード用
11pub async fn write_ip_lists_to_files(
12    country_code: &str,
13    ipv4_list: &BTreeSet<IpNet>,
14    ipv6_list: &BTreeSet<IpNet>,
15    format_enum: OutputFormat,
16) -> Result<(), AppError> {
17    let now_str = Local::now().format("%Y-%m-%d %H:%M:%S").to_string();
18    let safe_code = sanitize_identifier(&country_code.to_uppercase());
19
20    match format_enum {
21        OutputFormat::Txt => {
22            // IPv4
23            let ipv4_file = format!("IPv4_{}.txt", safe_code);
24            let header_v4 = make_header(&now_str, &safe_code, "N/A");
25            write_list_txt(&ipv4_file, ipv4_list, &header_v4).await?;
26
27            // IPv6
28            let ipv6_file = format!("IPv6_{}.txt", safe_code);
29            let header_v6 = make_header(&now_str, &safe_code, "N/A");
30            write_list_txt(&ipv6_file, ipv6_list, &header_v6).await?;
31        }
32        OutputFormat::Nft => {
33            // IPv4
34            let ipv4_file = format!("IPv4_{}.nft", safe_code);
35            let header_v4 = make_header(&now_str, &safe_code, "N/A");
36            write_list_nft(&ipv4_file, ipv4_list, &header_v4).await?;
37
38            // IPv6
39            let ipv6_file = format!("IPv6_{}.nft", safe_code);
40            let header_v6 = make_header(&now_str, &safe_code, "N/A");
41            write_list_nft(&ipv6_file, ipv6_list, &header_v6).await?;
42        }
43    }
44    Ok(())
45}
46
47/// IPv4/IPv6リストをファイルに書き出す
48/// AS番号用
49pub async fn write_as_ip_list_to_file(
50    as_number: &str,
51    family: IpFamily,
52    ipnets: &BTreeSet<IpNet>,
53    format_enum: OutputFormat,
54) -> Result<(), AppError> {
55    let now_str = Local::now().format("%Y-%m-%d %H:%M:%S").to_string();
56    let safe_as = sanitize_identifier(as_number);
57    let header = make_header(&now_str, "N/A", &safe_as);
58
59    match format_enum {
60        OutputFormat::Txt => {
61            let file_name = format!("AS_{}_{}.txt", safe_as, family.as_str());
62            write_list_txt(&file_name, ipnets, &header).await?;
63            debug_log(format!("Wrote TXT for AS_{} {}", safe_as, family.as_str()));
64        }
65        OutputFormat::Nft => {
66            let file_name = format!("AS_{}_{}.nft", safe_as, family.as_str());
67            write_list_nft(&file_name, ipnets, &header).await?;
68            debug_log(format!("Wrote NFT for AS_{} {}", safe_as, family.as_str()));
69        }
70    }
71    Ok(())
72}
73
74/// 国コード+AS番号の重複CIDRリストを書き出す
75pub async fn write_overlap_to_file(
76    country_code: &str,
77    as_number: &str,
78    overlaps: &BTreeSet<IpNet>,
79    format_enum: OutputFormat,
80) -> Result<(), AppError> {
81    use chrono::Local;
82    let safe_cc = sanitize_identifier(country_code);
83    let safe_as = sanitize_identifier(as_number);
84
85    let overlaps_v4: BTreeSet<IpNet> = overlaps
86        .iter()
87        .cloned()
88        .filter(|net| matches!(net, IpNet::V4(_)))
89        .collect();
90
91    let overlaps_v6: BTreeSet<IpNet> = overlaps
92        .iter()
93        .cloned()
94        .filter(|net| matches!(net, IpNet::V6(_)))
95        .collect();
96
97    if overlaps_v4.is_empty() && overlaps_v6.is_empty() {
98        debug_log(format!(
99            "No overlap found for country={} and AS={}",
100            country_code, as_number
101        ));
102        return Ok(());
103    }
104
105    match format_enum {
106        OutputFormat::Txt => {
107            if !overlaps_v4.is_empty() {
108                let filename_v4 = format!("overlap_{}_{}_IPv4.txt", safe_cc, safe_as);
109                let now_str = Local::now().format("%Y-%m-%d %H:%M:%S").to_string();
110                let header_v4 = make_header(&now_str, &safe_cc, &safe_as);
111                write_list_txt(&filename_v4, &overlaps_v4, &header_v4).await?;
112            }
113            if !overlaps_v6.is_empty() {
114                let filename_v6 = format!("overlap_{}_{}_IPv6.txt", safe_cc, safe_as);
115                let now_str = Local::now().format("%Y-%m-%d %H:%M:%S").to_string();
116                let header_v6 = make_header(&now_str, &safe_cc, &safe_as);
117                write_list_txt(&filename_v6, &overlaps_v6, &header_v6).await?;
118            }
119        }
120        OutputFormat::Nft => {
121            if !overlaps_v4.is_empty() {
122                let filename_v4 = format!("overlap_{}_{}_IPv4.nft", safe_cc, safe_as);
123                let now_str = Local::now().format("%Y-%m-%d %H:%M:%S").to_string();
124                let header_v4 = make_header(&now_str, &safe_cc, &safe_as);
125                write_list_nft(&filename_v4, &overlaps_v4, &header_v4).await?;
126            }
127            if !overlaps_v6.is_empty() {
128                let filename_v6 = format!("overlap_{}_{}_IPv6.nft", safe_cc, safe_as);
129                let now_str = Local::now().format("%Y-%m-%d %H:%M:%S").to_string();
130                let header_v6 = make_header(&now_str, &safe_cc, &safe_as);
131                write_list_nft(&filename_v6, &overlaps_v6, &header_v6).await?;
132            }
133        }
134    }
135
136    Ok(())
137}