use crate::config::{ColorWhen, Config, LogLevel};
use atty::Stream;
use clap::{Command, CommandFactory, Parser};
use clap_complete::{generate, Generator, Shell};
use std::collections::HashMap;
use std::{env, io};
use yansi::{Color, Paint};
#[derive(Parser, Debug)]
#[command(
author,
version,
about,
after_help = "Snazzy let you watch logs. \
It tries to be smart with Json logs by showing the levels, \n\
the message and the date in a nice and visual way.\n\
There is many more options to filter or highlight part of the logs or even launch some \n\
actions when a match is found. \n\n\
Try to stream some logs or specify a log file and let snazy, snazzy them!"
)]
struct Args {
#[arg(short = 'r', long, verbatim_doc_comment)]
pub regexp: Vec<String>,
#[arg(short = 'S', long)]
pub skip_line_regexp: Vec<String>,
#[arg(long, value_enum)]
shell_completion: Option<Shell>,
#[arg(short = 'f', long, verbatim_doc_comment)]
pub filter_levels: Vec<LogLevel>,
#[clap(
long,
short = 'c',
value_enum,
default_value_t = ColorWhen::Auto,
value_name = "when",
hide_possible_values = true,
verbatim_doc_comment
)]
pub color: ColorWhen,
#[arg(long, default_value = "%H:%M:%S", env = "SNAZY_TIME_FORMAT")]
pub time_format: String,
#[arg(
long,
verbatim_doc_comment,
default_value = "{namespace}/{pod}[{container}]",
env = "SNAZY_KAIL_PREFIX_FORMAT"
)]
pub kail_prefix_format: String,
#[arg(long, action(clap::ArgAction::SetTrue))]
pub kail_no_prefix: bool,
#[arg(long, action(clap::ArgAction::SetTrue), env = "SNAZY_LEVEL_SYMBOLS")]
pub level_symbols: bool,
#[arg(short = 'k', long, verbatim_doc_comment)]
pub json_keys: Vec<String>,
#[arg(long, verbatim_doc_comment)]
pub action_regexp: Option<String>,
#[arg(long, verbatim_doc_comment)]
pub action_command: Option<String>,
files: Option<Vec<String>>,
}
fn regexp_colorize(regexps: &[String]) -> HashMap<String, Color> {
let mut regexp_colours = HashMap::new();
let colours = vec![
Color::Cyan,
Color::Yellow,
Color::Red,
Color::Magenta,
Color::Blue,
];
for (i, regexp) in regexps.iter().enumerate() {
regexp_colours.insert((*regexp).to_string(), colours[i % colours.len()]);
}
regexp_colours
}
fn colouring(color: ColorWhen) -> bool {
let interactive_terminal = atty::is(Stream::Stdout);
match color {
ColorWhen::Always => true,
ColorWhen::Never => false,
ColorWhen::Auto => env::var_os("NO_COLOR").is_none() && interactive_terminal,
}
}
fn make_json_keys(json_keys: &[String]) -> HashMap<String, String> {
let ret: HashMap<String, String> = json_keys
.iter()
.map(|s| {
let mut parts = s.splitn(2, '=');
let key = parts.next().unwrap().to_string();
let value = parts.next().unwrap().to_string();
(key, value)
})
.collect();
ret
}
fn print_completions<G: Generator>(gen: G, cmd: &mut Command) {
generate(gen, cmd, cmd.get_name().to_string(), &mut io::stdout());
}
pub fn build_cli_config() -> Config {
let args = Args::parse();
if let Some(generator) = args.shell_completion {
let mut cmd = Args::command();
print_completions(generator, &mut cmd);
std::process::exit(0)
}
if !args.json_keys.is_empty() && args.json_keys.len() != 3 {
eprintln!("you should have multiple json-keys containning a match for the keys 'level', 'msg' and 'ts'");
std::process::exit(1);
}
let regexp_colours = regexp_colorize(&args.regexp);
let colouring = colouring(args.color);
if !colouring {
Paint::disable();
}
let json_keys = make_json_keys(&args.json_keys);
Config {
level_symbols: args.level_symbols,
kail_prefix_format: args.kail_prefix_format,
kail_no_prefix: args.kail_no_prefix,
time_format: args.time_format,
skip_line_regexp: args.skip_line_regexp,
filter_levels: args.filter_levels,
action_command: args.action_command,
action_regexp: args.action_regexp,
files: args.files,
regexp_colours,
colouring,
json_keys,
}
}