use std::io;
use std::process;
use clap::{CommandFactory, Parser, Subcommand};
use clap_complete::Shell;
use shortener::{Config, Logger, Shortener};
#[derive(Parser)]
#[command(
version,
long_version = shortener::version_string(),
)]
struct Cli {
#[arg(long)]
auth: bool,
#[arg(long, default_value_t = 8080)]
listen_port: u16,
#[arg(long)]
url_prefix: Option<String>,
#[arg(long)]
main_page: Option<String>,
#[arg(long, default_value_t = 6)]
code_length: usize,
#[arg(long, default_value = "shortener.db")]
sqlite_db: String,
#[arg(long, default_value = "access.log")]
log_file: String,
#[arg(long)]
trust_proxy: bool,
#[command(subcommand)]
command: Option<Command>,
}
#[derive(Subcommand)]
enum Command {
Completions {
shell: Shell,
},
}
impl From<Cli> for Config {
fn from(cli: Cli) -> Config {
let url_prefix = cli.url_prefix.unwrap_or_else(|| {
if cli.listen_port == 80 {
"http://localhost/".to_owned()
} else {
format!("http://localhost:{}/", cli.listen_port)
}
});
Config {
auth: cli.auth,
listen_port: cli.listen_port,
url_prefix,
main_page: cli.main_page,
code_length: cli.code_length,
sqlite_db: cli.sqlite_db,
log_file: cli.log_file,
trust_proxy: cli.trust_proxy,
}
}
}
#[tokio::main]
async fn main() {
let cli = Cli::parse();
if let Some(Command::Completions { shell }) = cli.command {
clap_complete::generate(
shell,
&mut Cli::command(),
"shortener",
&mut io::stdout(),
);
return;
}
let config: Config = cli.into();
if let Err(error) = Logger::init(&config.log_file) {
eprintln!("Failed to open log file: {}", error);
process::exit(1);
}
log::info!("Config: {:?}", config);
let shortener = match Shortener::new(config) {
Ok(shortener) => shortener,
Err(error) => {
log::error!("Failed to create shortener: {}", error);
process::exit(1);
}
};
log::info!("Starting HTTP server");
if let Err(error) = shortener.listen_and_serve().await {
log::error!("{}", error);
process::exit(1);
}
}