1use std::path::PathBuf;
6
7use clap::{Parser, Subcommand, ValueEnum};
8
9use crate::network::AdapterKind;
10
11#[derive(Debug, Parser)]
16#[command(name = "ddns-a")]
17#[command(version, about, long_about = None)]
18#[allow(clippy::struct_excessive_bools)] pub struct Cli {
20 #[command(subcommand)]
22 pub command: Option<Command>,
23
24 #[arg(long, global = true)]
26 pub url: Option<String>,
27
28 #[arg(long = "ip-version", value_enum, global = true)]
30 pub ip_version: Option<IpVersionArg>,
31
32 #[arg(long = "change-kind", value_enum, global = true)]
34 pub change_kind: Option<ChangeKindArg>,
35
36 #[arg(long)]
38 pub method: Option<String>,
39
40 #[arg(long = "header", value_name = "K=V")]
42 pub headers: Vec<String>,
43
44 #[arg(long)]
46 pub bearer: Option<String>,
47
48 #[arg(long = "body-template")]
50 pub body_template: Option<String>,
51
52 #[arg(long = "include-adapter", value_name = "PATTERN")]
54 pub include_adapters: Vec<String>,
55
56 #[arg(long = "exclude-adapter", value_name = "PATTERN")]
58 pub exclude_adapters: Vec<String>,
59
60 #[arg(long = "include-kind", value_name = "KIND", value_delimiter = ',')]
62 pub include_kinds: Vec<AdapterKindArg>,
63
64 #[arg(long = "exclude-kind", value_name = "KIND", value_delimiter = ',')]
66 pub exclude_kinds: Vec<AdapterKindArg>,
67
68 #[arg(long = "poll-interval")]
70 pub poll_interval: Option<u64>,
71
72 #[arg(long = "poll-only")]
74 pub poll_only: bool,
75
76 #[arg(long = "retry-max")]
78 pub retry_max: Option<u32>,
79
80 #[arg(long = "retry-delay")]
82 pub retry_delay: Option<u64>,
83
84 #[arg(long, short)]
86 pub config: Option<PathBuf>,
87
88 #[arg(long = "state-file")]
90 pub state_file: Option<PathBuf>,
91
92 #[arg(long)]
94 pub dry_run: bool,
95
96 #[arg(long, short)]
98 pub verbose: bool,
99}
100
101#[derive(Debug, Subcommand)]
103pub enum Command {
104 Init {
106 #[arg(long, short, default_value = "ddns-a.toml")]
108 output: PathBuf,
109 },
110}
111
112#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
114pub enum IpVersionArg {
115 #[value(name = "ipv4")]
117 V4,
118 #[value(name = "ipv6")]
120 V6,
121 #[value(name = "both")]
123 Both,
124}
125
126#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
130pub enum ChangeKindArg {
131 #[value(name = "added")]
133 Added,
134 #[value(name = "removed")]
136 Removed,
137 #[value(name = "both")]
139 Both,
140}
141
142impl From<IpVersionArg> for crate::network::IpVersion {
143 fn from(arg: IpVersionArg) -> Self {
144 match arg {
145 IpVersionArg::V4 => Self::V4,
146 IpVersionArg::V6 => Self::V6,
147 IpVersionArg::Both => Self::Both,
148 }
149 }
150}
151
152impl From<ChangeKindArg> for crate::monitor::ChangeKind {
153 fn from(arg: ChangeKindArg) -> Self {
154 match arg {
155 ChangeKindArg::Added => Self::Added,
156 ChangeKindArg::Removed => Self::Removed,
157 ChangeKindArg::Both => Self::Both,
158 }
159 }
160}
161
162#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ValueEnum)]
168#[value(rename_all = "lowercase")]
169pub enum AdapterKindArg {
170 Ethernet,
172 Wireless,
174 Virtual,
176 Loopback,
178}
179
180impl From<AdapterKindArg> for AdapterKind {
181 fn from(arg: AdapterKindArg) -> Self {
182 match arg {
183 AdapterKindArg::Ethernet => Self::Ethernet,
184 AdapterKindArg::Wireless => Self::Wireless,
185 AdapterKindArg::Virtual => Self::Virtual,
186 AdapterKindArg::Loopback => Self::Loopback,
187 }
188 }
189}
190
191impl Cli {
192 #[must_use]
194 pub fn parse_args() -> Self {
195 Self::parse()
196 }
197
198 pub fn parse_from_iter<I, T>(iter: I) -> Self
200 where
201 I: IntoIterator<Item = T>,
202 T: Into<std::ffi::OsString> + Clone,
203 {
204 Self::parse_from(iter)
205 }
206
207 #[must_use]
209 pub const fn is_init(&self) -> bool {
210 matches!(self.command, Some(Command::Init { .. }))
211 }
212}