1use crate::upstream::Upstream;
2use std::net::SocketAddr;
3
4#[repr(C)]
5#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)]
6pub enum ArgVerbosity {
7 Off = 0,
8 Error,
9 Warn,
10 #[default]
11 Info,
12 Debug,
13 Trace,
14}
15
16#[cfg(target_os = "android")]
17impl TryFrom<jni::sys::jint> for ArgVerbosity {
18 type Error = std::io::Error;
19 fn try_from(value: jni::sys::jint) -> Result<Self, <Self as TryFrom<jni::sys::jint>>::Error> {
20 match value {
21 0 => Ok(ArgVerbosity::Off),
22 1 => Ok(ArgVerbosity::Error),
23 2 => Ok(ArgVerbosity::Warn),
24 3 => Ok(ArgVerbosity::Info),
25 4 => Ok(ArgVerbosity::Debug),
26 5 => Ok(ArgVerbosity::Trace),
27 _ => Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "Invalid verbosity level")),
28 }
29 }
30}
31
32impl From<ArgVerbosity> for log::LevelFilter {
33 fn from(verbosity: ArgVerbosity) -> Self {
34 match verbosity {
35 ArgVerbosity::Off => log::LevelFilter::Off,
36 ArgVerbosity::Error => log::LevelFilter::Error,
37 ArgVerbosity::Warn => log::LevelFilter::Warn,
38 ArgVerbosity::Info => log::LevelFilter::Info,
39 ArgVerbosity::Debug => log::LevelFilter::Debug,
40 ArgVerbosity::Trace => log::LevelFilter::Trace,
41 }
42 }
43}
44
45impl From<log::Level> for ArgVerbosity {
46 fn from(level: log::Level) -> Self {
47 match level {
48 log::Level::Error => ArgVerbosity::Error,
49 log::Level::Warn => ArgVerbosity::Warn,
50 log::Level::Info => ArgVerbosity::Info,
51 log::Level::Debug => ArgVerbosity::Debug,
52 log::Level::Trace => ArgVerbosity::Trace,
53 }
54 }
55}
56
57impl std::fmt::Display for ArgVerbosity {
58 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59 match self {
60 ArgVerbosity::Off => write!(f, "off"),
61 ArgVerbosity::Error => write!(f, "error"),
62 ArgVerbosity::Warn => write!(f, "warn"),
63 ArgVerbosity::Info => write!(f, "info"),
64 ArgVerbosity::Debug => write!(f, "debug"),
65 ArgVerbosity::Trace => write!(f, "trace"),
66 }
67 }
68}
69
70#[derive(clap::Parser, Debug, Clone, PartialEq, Eq, Default)]
72#[command(author, version, about = "A lightweight DNS-over-HTTPS proxy", long_about = None)]
73pub struct Args {
74 #[arg(short, long, value_name = "IP:port", default_value = "127.0.0.1:53")]
76 pub bind: Vec<SocketAddr>,
77
78 #[arg(short, long, value_name = "URL", default_value = "https://1.1.1.1/dns-query")]
80 pub upstream_urls: Vec<String>,
81
82 #[arg(short, long, value_name = "level", value_enum, default_value = "info")]
84 pub verbosity: ArgVerbosity,
85
86 #[cfg(target_os = "windows")]
88 #[arg(long)]
89 pub service: bool,
90}
91
92impl Args {
93 pub fn bind<T: Into<SocketAddr>>(&mut self, bind: T) -> &mut Self {
94 self.bind.push(bind.into());
95 self
96 }
97
98 pub fn upstream_url<T: Into<String>>(&mut self, url: T) -> &mut Self {
99 self.upstream_urls.push(url.into());
100 self
101 }
102
103 pub fn verbosity(&mut self, verbosity: ArgVerbosity) -> &mut Self {
104 self.verbosity = verbosity;
105 self
106 }
107
108 pub fn parse() -> Args {
110 <Args as clap::Parser>::parse()
111 }
112
113 pub fn upstreams<'a>(&'a self, client: &'a reqwest::Client) -> Vec<Upstream<'a>> {
115 self.upstream_urls.iter().map(|url| Upstream::new(client, url)).collect()
116 }
117}