1use anyhow::anyhow;
2use std::ops::Deref;
3use std::str::FromStr;
4
5#[derive(clap::Subcommand)]
6pub enum Commands {
7 #[clap(about = "uri tools", alias = "url")]
8 Uri {
9 #[clap(subcommand)]
10 command: uri::UriCommand,
11 },
12 #[clap(about = "json tools")]
13 Json {
14 #[clap(subcommand)]
15 command: json::JsonCommand,
16 },
17 #[clap(about = "time tools")]
18 Time {
19 #[clap(subcommand)]
20 command: time::TimeCommand,
21 },
22 #[clap(name = "qrcode", about = "qrcode generator tools, alias 'qr'", alias = "qr")]
23 QrCode(qrcode::QrCodeArgs),
24 #[clap(about = "base64 tools, alias 'b64'", alias = "b64")]
25 Base64 {
26 #[clap(subcommand)]
27 command: base64::Base64Command,
28 },
29}
30
31pub trait Command {
32 fn run(&self) -> crate::Result<()>;
33}
34
35impl Command for Commands {
36 fn run(&self) -> crate::Result<()> {
37 match self {
38 Commands::Uri { command } => command.run(),
39 Commands::Json { command } => command.run(),
40 Commands::Time { command } => command.run(),
41 Commands::QrCode(args) => args.run(),
42 Commands::Base64 { command } => command.run(),
43 }
44 }
45}
46
47pub mod base64;
48mod http_parser;
49pub mod json;
50pub mod qrcode;
51pub mod time;
52pub mod uri;
53
54#[cfg(feature = "read_stdin")]
55fn read_stdin() -> Option<String> {
56 use std::io::{BufRead, IsTerminal};
57 let stdin = std::io::stdin().lock();
58 if stdin.is_terminal() {
59 None
60 } else {
61 let mut lines = vec![];
62 for line in stdin.lines() {
63 match line {
64 Ok(line) => {
65 let _ = lines.push(line);
66 }
67 Err(err) => {
68 log::error!("read from stdin failed, {}", err);
69 break;
70 }
71 }
72 }
73 let string = lines.join("\n");
74 Some(string)
75 }
76}
77
78#[derive(Debug, Clone)]
79pub struct StringInput(String);
80
81impl Deref for StringInput {
82 type Target = str;
83 fn deref(&self) -> &Self::Target {
84 &self.0
85 }
86}
87
88impl FromStr for StringInput {
89 type Err = anyhow::Error;
90 fn from_str(value: &str) -> Result<Self, Self::Err> {
91 if value.is_empty()
92 && let Some(string) = read_stdin()
93 {
94 if !string.is_empty() {
95 return Ok(Self::from_str(&string)?);
96 }
97 }
98 if value.is_empty() {
99 Err(anyhow!("Invalid input"))
100 } else {
101 Ok(Self(value.to_string()))
102 }
103 }
104}
105
106#[cfg(not(feature = "read_stdin"))]
107fn read_stdin() -> Option<String> {
108 None
109}