fire_scope/
cli.rs

1use clap::Parser;
2
3fn parse_country_code(s: &str) -> Result<String, String> {
4    let upper = s.to_ascii_uppercase();
5    let valid = upper.chars().all(|c| c.is_ascii_alphabetic());
6    if !valid {
7        return Err("Country code must be alphabetic (A-Z)".into());
8    }
9    let len = upper.len();
10    if !(len == 2 || len == 3) {
11        return Err("Country code length must be 2 or 3".into());
12    }
13    Ok(upper)
14}
15
16/// CLIの定義
17#[derive(Parser, Debug)]
18#[command(
19    author,
20    version,
21    about = "This tool can be used to obtain IP addresses by country or by AS number."
22)]
23pub struct Cli {
24    #[arg(
25        short = 'c',
26        long = "country",
27        required_unless_present_any = ["as_numbers", "overlap"],
28        required = false,
29        num_args = 1..,
30        value_parser = parse_country_code,
31        help = "Specify the country codes.\nExample: jp br us"
32    )]
33    pub country_codes: Option<Vec<String>>,
34
35    #[arg(
36        short = 'a',
37        long = "as-number",
38        required_unless_present_any = ["country_codes", "overlap"],
39        required = false,
40        value_parser = clap::value_parser!(u32),
41        num_args = 1..,
42        help = "Specify AS numbers.\nExample: 0000 1234"
43    )]
44    pub as_numbers: Option<Vec<u32>>,
45
46    #[arg(
47        short = 'o',
48        long = "overlap",
49        help = "Write down the IP addresses of the overlapping country and AS numbers in a file of your choice.\nBoth the -c and -a arguments must be specified.",
50        required = false,
51        default_value = "false",
52        requires("country_codes"),
53        requires("as_numbers")
54    )]
55    pub overlap: bool,
56
57    #[arg(
58        short = 'f',
59        long = "format",
60        default_value = "txt",
61        required = false,
62        hide_default_value = true,
63        value_parser = ["txt", "nft"],
64        help = "Select output format: 'txt' or 'nft'.\ndefault: txt"
65    )]
66    pub output_format: String,
67
68    #[arg(
69        long = "max-retries",
70        help = "Maximum HTTP retry attempts for downloads.",
71        required = false,
72        default_value_t = 6u32,
73        value_parser = clap::value_parser!(u32)
74    )]
75    pub max_retries: u32,
76
77    #[arg(
78        long = "max-backoff-sec",
79        help = "Cap for exponential backoff seconds per retry.",
80        required = false,
81        default_value_t = 16u64,
82        value_parser = clap::value_parser!(u64)
83    )]
84    pub max_backoff_sec: u64,
85
86    #[arg(
87        long = "http-timeout-secs",
88        help = "HTTP request total timeout in seconds.",
89        required = false,
90        default_value_t = 20u64,
91        value_parser = clap::value_parser!(u64)
92    )]
93    pub http_timeout_secs: u64,
94
95    #[arg(
96        long = "connect-timeout-secs",
97        help = "HTTP connect timeout in seconds.",
98        required = false,
99        default_value_t = 10u64,
100        value_parser = clap::value_parser!(u64)
101    )]
102    pub connect_timeout_secs: u64,
103
104    #[arg(
105        long = "concurrency",
106        short = 'C',
107        help = "Max concurrent AS queries.",
108        required = false,
109        default_value_t = 5usize,
110        value_parser = clap::value_parser!(usize)
111    )]
112    pub concurrency: usize,
113
114    #[arg(
115        long = "continue-on-partial",
116        help = "Continue with successfully downloaded RIR files even if some downloads fail.",
117        required = false,
118        default_value_t = false
119    )]
120    pub continue_on_partial: bool,
121
122    #[arg(
123        long = "debug",
124        short = 'd',
125        help = "Enable verbose debug output to stderr.",
126        required = false,
127        default_value_t = false
128    )]
129    pub debug: bool,
130}