#![deny(clippy::expect_used)]
#![deny(clippy::unwrap_used)]
mod build;
mod cmd;
mod common;
mod config;
mod hooks;
mod pipelines;
mod processing;
mod proxy;
mod serve;
mod tls;
mod tools;
mod version;
mod watch;
mod ws;
use anyhow::{Context, Result};
use clap::{ArgAction, Parser, Subcommand, ValueEnum};
use common::STARTING;
use std::io::IsTerminal;
use std::path::PathBuf;
use std::process::ExitCode;
use tracing_subscriber::prelude::*;
#[tokio::main]
async fn main() -> Result<ExitCode> {
let cli = Trunk::parse();
let colored = init_color(&cli);
tracing_subscriber::registry()
.with(eval_logging(&cli))
.with(
tracing_subscriber::fmt::layer()
.with_ansi(colored)
.with_target(false)
.with_level(true)
.compact(),
)
.try_init()
.context("error initializing logging")?;
tracing::info!(
"{}Starting {} {}",
STARTING,
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_VERSION")
);
Ok(match cli.run().await {
Err(err) => {
tracing::error!("{err}");
for (n, cause) in err.chain().enumerate().skip(1) {
tracing::info!(" {n}: {cause}");
}
ExitCode::FAILURE
}
Ok(()) => ExitCode::SUCCESS,
})
}
fn init_color(cli: &Trunk) -> bool {
if cli.no_color {
return false;
}
let colored = match cli.color {
ColorMode::Always => true,
ColorMode::Never => false,
ColorMode::Auto => std::io::stdout().is_terminal(),
};
#[cfg(windows)]
if colored {
if let Err(err) = nu_ansi_term::enable_ansi_support() {
eprintln!("error enabling ANSI support: {:?}", err);
}
}
#[allow(clippy::let_and_return)]
colored
}
fn eval_logging(cli: &Trunk) -> tracing_subscriber::EnvFilter {
if let Some(directives) = &cli.log {
return tracing_subscriber::EnvFilter::new(directives);
}
let prefer_silence = cli.prefer_silence();
let silent = cli.quiet || prefer_silence;
let directives = match (cli.verbose, silent) {
(_, true) => "error,trunk=warn",
(0, false) => "error,trunk=info",
(1, false) => "error,trunk=debug",
(_, false) => "error,trunk=trace",
};
tracing_subscriber::EnvFilter::new(directives)
}
#[derive(Parser)]
#[command(about, author, version)]
struct Trunk {
#[command(subcommand)]
action: TrunkSubcommands,
#[arg(long, env = "TRUNK_CONFIG", global(true))]
pub config: Option<PathBuf>,
#[arg(short, long, global(true), action=ArgAction::Count)]
pub verbose: u8,
#[arg(short, long, global(true), conflicts_with("verbose"))]
pub quiet: bool,
#[arg(long, global(true), conflicts_with_all(["verbose", "quiet"]), env("RUST_LOG"))]
pub log: Option<String>,
#[arg(long, global(true), env = "TRUNK_SKIP_VERSION_CHECK")]
pub skip_version_check: bool,
#[arg(long, global(true), env = "TRUNK_OFFLINE")]
#[arg(default_missing_value = "true", num_args=0..=1)]
pub offline: Option<bool>,
#[arg(long, env = "TRUNK_COLOR", global(true), value_enum, conflicts_with = "no_color", default_value_t = ColorMode::Auto)]
pub color: ColorMode,
#[arg(long, env = "NO_COLOR", global(true))]
pub no_color: bool,
}
impl Trunk {
pub fn prefer_silence(&self) -> bool {
#[allow(clippy::match_like_matches_macro)]
match self.action {
TrunkSubcommands::Config(_) => true,
TrunkSubcommands::Tools(_) => true,
_ => false,
}
}
}
#[derive(Clone, Debug, Default, ValueEnum)]
#[value(rename_all = "lower")]
enum ColorMode {
#[default]
Auto,
Always,
Never,
}
impl Trunk {
#[tracing::instrument(level = "trace", skip(self))]
pub async fn run(self) -> Result<()> {
version::update_check(self.skip_version_check | self.offline.unwrap_or_default());
match self.action {
TrunkSubcommands::Build(inner) => inner.run(self.config).await,
TrunkSubcommands::Clean(inner) => inner.run(self.config).await,
TrunkSubcommands::Serve(inner) => inner.run(self.config).await,
TrunkSubcommands::Watch(inner) => inner.run(self.config).await,
TrunkSubcommands::Config(inner) => inner.run(self.config).await,
TrunkSubcommands::Tools(inner) => inner.run(self.config).await,
}
}
}
#[derive(Subcommand)]
enum TrunkSubcommands {
Build(cmd::build::Build),
Watch(cmd::watch::Watch),
Serve(cmd::serve::Serve),
Clean(cmd::clean::Clean),
Config(cmd::config::Config),
Tools(cmd::tools::Tools),
}
#[cfg(test)]
mod tests {
use crate::Trunk;
#[test]
fn verify_cli() {
use clap::CommandFactory;
Trunk::command().debug_assert();
}
}