mod completions;
mod keywords;
mod styles;
use crate::cli::completions::generate_shell_completions_and_exit_or_continue;
use crate::cli::keywords::get_keywords_from_cli;
use crate::cli::styles::get_styles;
use crate::config::{Source, Target, get_io_config};
use crate::highlighter_builder;
use crate::highlighter_builder::builtins::get_builtin_keywords;
use crate::highlighter_builder::groups;
use crate::theme::reader;
use anyhow::Result;
use clap::{ArgAction, Parser, ValueEnum};
use nu_ansi_term::Style;
use std::error::Error;
use std::io::{IsTerminal, stdin};
use std::path::PathBuf;
use tailspin::Highlighter;
#[derive(Parser)]
#[command(
name = "tspin",
version,
about,
author,
styles = get_styles(),
max_term_width = 120,
disable_help_flag = true,
arg(clap::Arg::new("help")
.short('h')
.long("help")
.help("Print help")
.action(ArgAction::HelpShort)),
)]
pub struct Arguments {
#[clap(name = "FILE", value_hint = clap::ValueHint::FilePath)]
pub file_path: Option<PathBuf>,
#[clap(short = 'f', long = "follow")]
pub follow: bool,
#[clap(short = 'p', long = "print")]
pub to_stdout: bool,
#[clap(long = "config-path")]
pub config_path: Option<PathBuf>,
#[clap(short = 'e', long = "exec")]
pub exec: Option<String>,
#[arg(long = "highlight", value_parser = parse_highlight)]
pub color_word: Vec<(KeywordColor, Vec<String>)>,
#[clap(long = "enable", value_enum, use_value_delimiter = true)]
pub enabled_highlighters: Vec<HighlighterGroup>,
#[clap(long = "disable", value_enum, use_value_delimiter = true)]
pub disabled_highlighters: Vec<HighlighterGroup>,
#[clap(long = "extras", value_enum, use_value_delimiter = true)]
pub extras: Vec<Extras>,
#[clap(long = "disable-builtin-keywords")]
pub disable_builtin_keywords: bool,
#[clap(long = "pager", env = "TAILSPIN_PAGER")]
pub pager: Option<String>,
#[clap(long = "generate-bash-completions", hide = true)]
pub generate_bash_completions: bool,
#[clap(long = "generate-fish-completions", hide = true)]
pub generate_fish_completions: bool,
#[clap(long = "generate-zsh-completions", hide = true)]
pub generate_zsh_completions: bool,
}
fn parse_highlight(s: &str) -> Result<(KeywordColor, Vec<String>), Box<dyn Error + Send + Sync>> {
let (color_str, words_str) = s
.split_once(':')
.ok_or_else(|| format!("Expected format COLOR:word1,word2,... found `{s}`"))?;
let color = KeywordColor::from_str(color_str, true)?;
let words = words_str.split(',').map(str::to_owned).collect();
Ok((color, words))
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Hash)]
pub enum KeywordColor {
Red,
Green,
Yellow,
Blue,
Magenta,
Cyan,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
pub enum Extras {
Ipv6,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
pub enum HighlighterGroup {
Numbers,
Urls,
Emails,
Pointers,
Dates,
Paths,
Quotes,
KeyValuePairs,
Uuids,
Ipv4,
Processes,
Json,
}
pub struct FullConfig {
pub source: Source,
pub target: Target,
pub highlighter: Highlighter,
}
pub fn get_config() -> Result<FullConfig> {
let cli = Arguments::parse();
generate_shell_completions_and_exit_or_continue(&cli);
let std_in_has_no_data = stdin().is_terminal();
if cli.file_path.is_none() && cli.exec.is_none() && std_in_has_no_data {
let style = Style::new().fg(nu_ansi_term::Color::Cyan);
println!("Missing filename ({} for help)", style.paint("tspin --help"));
std::process::exit(0);
}
let io_config = get_io_config(&cli)?;
let mut highlighter_groups = groups::get_highlighter_groups(&cli.enabled_highlighters, &cli.disabled_highlighters)?;
highlighter_groups.ip_v6 = cli.extras.contains(&Extras::Ipv6);
let theme = reader::parse_theme(cli.config_path.as_ref())?;
let keywords_builtin = get_builtin_keywords(cli.disable_builtin_keywords);
let keywords_from_toml = theme.keywords.clone();
let keywords_from_cli = get_keywords_from_cli(&cli);
let keywords: Vec<_> = vec![]
.into_iter()
.chain(keywords_builtin)
.chain(keywords_from_toml)
.chain(keywords_from_cli)
.collect();
let highlighter = highlighter_builder::get_highlighter(highlighter_groups, theme, keywords)?;
Ok(FullConfig {
source: io_config.source,
target: io_config.target,
highlighter,
})
}
#[test]
fn verify_app() {
use clap::CommandFactory;
Arguments::command().debug_assert();
}