diff --git a/src/main.rs b/src/main.rs index 2fc0dab..4744090 100644 --- a/src/main.rs +++ b/src/main.rs @@ -61,7 +61,8 @@ async fn main() -> Result<()> { // `echo --version` / `-V` prints the build banner (version, git revision, build // date, toolchain, target) and exits. if matches!(argv.get(1).map(String::as_str), Some("--version") | Some("-V") | Some("version")) { - println!("{}", version::banner()); + use std::io::IsTerminal; + println!("{}", version::banner(std::io::stdout().is_terminal())); return Ok(()); } if argv.get(1).map(String::as_str) == Some("import") { @@ -95,6 +96,11 @@ async fn main() -> Result<()> { } let path = std::env::args().nth(1).unwrap_or_else(|| "config.toml".to_string()); + // On an interactive run, greet with the full banner; under systemd (stderr is + // journald, not a tty) skip it and let the structured log line below stand. + if std::io::IsTerminal::is_terminal(&std::io::stderr()) { + eprintln!("{}", version::banner(true)); + } tracing::info!(revision = %version::revision(), built = %version::built(), rustc = version::RUSTC, "starting {}", version::short()); let cfg = config::Config::load(&path)?; let ts = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs(); diff --git a/src/version.rs b/src/version.rs index 871a271..d65197e 100644 --- a/src/version.rs +++ b/src/version.rs @@ -33,22 +33,48 @@ pub fn short() -> String { format!("echo {VERSION} ({})", revision()) } -/// The full multi-line banner for `echo --version`. -pub fn banner() -> String { +// ASCII wordmark (figlet "standard", "echo"). +const LOGO: [&str; 5] = [ + r" _ ", + r" ___ ___| |__ ___ ", + r" / _ \/ __| '_ \ / _ \ ", + r" | __/ (__| | | | (_) |", + r" \___|\___|_| |_|\___/ ", +]; + +/// The full banner for `echo --version` / an interactive startup. `color` emits +/// ANSI styling (only when writing to a terminal). +pub fn banner(color: bool) -> String { + let (logo, name, dim, reset) = if color { + ("\x1b[38;5;44m", "\x1b[1;38;5;51m", "\x1b[2m", "\x1b[0m") + } else { + ("", "", "", "") + }; let commit = if COMMIT_DATE.is_empty() { String::new() } else { - format!(" ({COMMIT_DATE})") + format!(" {dim}({COMMIT_DATE}){reset}") }; - format!( - "echo \u{2014} federated IRC services\n \ - version: {VERSION}\n \ - revision: {rev}{commit}\n \ - built: {built} ({profile})\n \ - rustc: {RUSTC}\n \ - target: {TARGET}", - rev = revision(), - built = built(), - profile = profile(), - ) + // Identity text aligned against the middle rows of the logo. + let side = [ + String::new(), + format!("{name}echo{reset} {dim}services{reset}"), + format!("{dim}federated IRC services{reset}"), + String::new(), + String::new(), + ]; + let mut out = String::from("\n"); + for i in 0..LOGO.len() { + out.push_str(&format!(" {logo}{:<24}{reset} {}\n", LOGO[i], side[i])); + } + out.push('\n'); + let mut row = |label: &str, value: String| { + out.push_str(&format!(" {dim}{label:<9}{reset}{value}\n")); + }; + row("version", VERSION.to_string()); + row("revision", format!("{}{commit}", revision())); + row("built", format!("{} {dim}ยท{reset} {}", built(), profile())); + row("rustc", RUSTC.strip_prefix("rustc ").unwrap_or(RUSTC).to_string()); + row("target", TARGET.to_string()); + out }