logged_tcp_proxy/
args.rs

1use clap::builder::PossibleValue;
2use clap::Parser;
3use clap::ValueEnum;
4use env_logger::TimestampPrecision as EnvLoggerTimestampPrecision;
5use logged_stream::BinaryFormatter;
6use logged_stream::BufferFormatter;
7use logged_stream::DecimalFormatter;
8use logged_stream::LowercaseHexadecimalFormatter;
9use logged_stream::OctalFormatter;
10use logged_stream::UppercaseHexadecimalFormatter;
11use std::convert::From;
12use std::fmt;
13use std::net;
14use std::str::FromStr;
15
16#[derive(Debug, Clone, Copy)]
17pub enum LoggingLevel {
18    Trace,
19    Debug,
20    Info,
21    Warn,
22    Error,
23    Off,
24}
25
26impl ValueEnum for LoggingLevel {
27    fn value_variants<'a>() -> &'a [Self] {
28        &[
29            Self::Trace,
30            Self::Debug,
31            Self::Info,
32            Self::Warn,
33            Self::Error,
34            Self::Off,
35        ]
36    }
37
38    fn to_possible_value(&self) -> Option<PossibleValue> {
39        Some(match self {
40            Self::Trace => PossibleValue::new("trace"),
41            Self::Debug => PossibleValue::new("debug"),
42            Self::Info => PossibleValue::new("info"),
43            Self::Warn => PossibleValue::new("warn"),
44            Self::Error => PossibleValue::new("error"),
45            Self::Off => PossibleValue::new("off"),
46        })
47    }
48}
49
50impl FromStr for LoggingLevel {
51    type Err = String;
52
53    fn from_str(s: &str) -> Result<Self, Self::Err> {
54        for variant in Self::value_variants() {
55            if variant.to_possible_value().unwrap().matches(s, false) {
56                return Ok(*variant);
57            }
58        }
59        Err(format!("Invalid variant: {}", s))
60    }
61}
62
63impl fmt::Display for LoggingLevel {
64    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65        self.to_possible_value()
66            .expect("no values are skipped")
67            .get_name()
68            .fmt(f)
69    }
70}
71
72#[derive(Debug, Clone, Copy)]
73pub enum PayloadFormatingKind {
74    Decimal,
75    LowerHex,
76    UpperHex,
77    Binary,
78    Octal,
79}
80
81impl ValueEnum for PayloadFormatingKind {
82    fn value_variants<'a>() -> &'a [Self] {
83        &[
84            Self::Decimal,
85            Self::LowerHex,
86            Self::UpperHex,
87            Self::Binary,
88            Self::Octal,
89        ]
90    }
91
92    fn to_possible_value(&self) -> Option<PossibleValue> {
93        Some(match self {
94            Self::Decimal => PossibleValue::new("decimal"),
95            Self::LowerHex => PossibleValue::new("lowerhex"),
96            Self::UpperHex => PossibleValue::new("upperhex"),
97            Self::Binary => PossibleValue::new("binary"),
98            Self::Octal => PossibleValue::new("octal"),
99        })
100    }
101}
102
103impl FromStr for PayloadFormatingKind {
104    type Err = String;
105
106    fn from_str(s: &str) -> Result<Self, Self::Err> {
107        for variant in Self::value_variants() {
108            if variant.to_possible_value().unwrap().matches(s, false) {
109                return Ok(*variant);
110            }
111        }
112        Err(format!("Invalid variant: {}", s))
113    }
114}
115
116impl fmt::Display for PayloadFormatingKind {
117    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118        self.to_possible_value()
119            .expect("no values are skipped")
120            .get_name()
121            .fmt(f)
122    }
123}
124
125pub fn get_formatter_by_kind(
126    kind: PayloadFormatingKind,
127    separator: &str,
128) -> Box<dyn BufferFormatter> {
129    match kind {
130        PayloadFormatingKind::Decimal => Box::new(DecimalFormatter::new(Some(separator))),
131        PayloadFormatingKind::LowerHex => {
132            Box::new(LowercaseHexadecimalFormatter::new(Some(separator)))
133        }
134        PayloadFormatingKind::UpperHex => {
135            Box::new(UppercaseHexadecimalFormatter::new(Some(separator)))
136        }
137        PayloadFormatingKind::Binary => Box::new(BinaryFormatter::new(Some(separator))),
138        PayloadFormatingKind::Octal => Box::new(OctalFormatter::new(Some(separator))),
139    }
140}
141
142#[derive(Debug, Clone, Copy)]
143pub enum TimestampPrecision {
144    Seconds,
145    Milliseconds,
146    Microseconds,
147    Nanoseconds,
148}
149
150impl ValueEnum for TimestampPrecision {
151    fn value_variants<'a>() -> &'a [Self] {
152        &[
153            Self::Seconds,
154            Self::Milliseconds,
155            Self::Microseconds,
156            Self::Nanoseconds,
157        ]
158    }
159
160    fn to_possible_value(&self) -> Option<PossibleValue> {
161        Some(match self {
162            Self::Seconds => PossibleValue::new("seconds"),
163            Self::Milliseconds => PossibleValue::new("milliseconds"),
164            Self::Microseconds => PossibleValue::new("microseconds"),
165            Self::Nanoseconds => PossibleValue::new("nanoseconds"),
166        })
167    }
168}
169
170impl FromStr for TimestampPrecision {
171    type Err = String;
172
173    fn from_str(s: &str) -> Result<Self, Self::Err> {
174        for variant in Self::value_variants() {
175            if variant.to_possible_value().unwrap().matches(s, false) {
176                return Ok(*variant);
177            }
178        }
179        Err(format!("Invalid variant: {}", s))
180    }
181}
182
183impl fmt::Display for TimestampPrecision {
184    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
185        self.to_possible_value()
186            .expect("no values are skipped")
187            .get_name()
188            .fmt(f)
189    }
190}
191
192impl From<TimestampPrecision> for EnvLoggerTimestampPrecision {
193    fn from(precision: TimestampPrecision) -> Self {
194        match precision {
195            TimestampPrecision::Seconds => EnvLoggerTimestampPrecision::Seconds,
196            TimestampPrecision::Milliseconds => EnvLoggerTimestampPrecision::Millis,
197            TimestampPrecision::Microseconds => EnvLoggerTimestampPrecision::Micros,
198            TimestampPrecision::Nanoseconds => EnvLoggerTimestampPrecision::Nanos,
199        }
200    }
201}
202
203#[derive(Debug, Clone, Parser)]
204#[command(next_line_help = true)]
205#[command(author, version, about, long_about = None)]
206pub struct Arguments {
207    /// Application logging level.
208    #[arg(short, long, default_value = "debug")]
209    pub level: LoggingLevel,
210    /// Address on which TCP listener should be binded.
211    #[arg(short, long)]
212    pub bind_listener_addr: net::SocketAddr,
213    /// Address of remote server.
214    #[arg(short, long)]
215    pub remote_addr: net::SocketAddr,
216    /// Incoming connection reading timeout.
217    #[arg(short, long, default_value = "60")]
218    pub timeout: u64,
219    /// Formatting of console payload output,
220    #[arg(short, long, default_value = "lowerhex")]
221    pub formatting: PayloadFormatingKind,
222    /// Console payload output bytes separator.
223    #[arg(short, long, default_value = ":")]
224    pub separator: String,
225    /// Timestamp precision.
226    #[arg(short, long, default_value = "seconds")]
227    pub precision: TimestampPrecision,
228}