use anstyle::{AnsiColor, Style};
use clap::{ColorChoice, Parser};
use std::{
env,
io::{self, Write},
time::Duration,
};
use tokio::time::sleep;
const GREEN: Style = Style::new().fg_color(Some(anstyle::Color::Ansi(AnsiColor::Green)));
const RED: Style = Style::new().fg_color(Some(anstyle::Color::Ansi(AnsiColor::Red)));
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let args = Args::parse();
let mut vars: Vec<(String, String)> = if args.vars.is_empty() {
env::vars_os()
.filter_map(|(k, v)| {
let key = k.into_string().ok()?;
if args.all || !key.starts_with("_") {
return Some((key, v.into_string().ok()?));
}
None
})
.collect()
} else {
let mut vars = Vec::new();
for k in &args.vars {
let Some(v) = env::var(k).ok() else {
continue;
};
vars.push((k.clone(), v));
}
vars
};
vars.sort_by(|a, b| a.0.cmp(&b.0));
let padding = vars.len().to_string().len();
let mut stdout = anstream::AutoStream::new(io::stdout(), args.color());
let mut stderr = anstream::AutoStream::new(io::stderr(), args.color());
for (i, (key, value)) in vars.into_iter().enumerate() {
if let Some(delay) = args.delay {
sleep(Duration::from_millis(delay)).await;
}
let line = format!("{key}={value}");
match i % 2 {
0 => writeln!(stdout, "{GREEN}stdout {:>padding$}{GREEN:#}: {line}", i + 1)?,
_ => writeln!(stderr, "{RED}stderr {:>padding$}{RED:#}: {line}", i + 1)?,
}
}
Ok(())
}
#[derive(Debug, Parser)]
struct Args {
#[arg(long)]
all: bool,
#[arg(short = 'c', long, default_value_t)]
color: ColorChoice,
#[arg(long)]
delay: Option<u64>,
#[arg(value_name = "VARIABLES", trailing_var_arg = true)]
vars: Vec<String>,
}
impl Args {
fn color(&self) -> anstream::ColorChoice {
match self.color {
ColorChoice::Always => anstream::ColorChoice::Always,
ColorChoice::Auto => anstream::ColorChoice::Auto,
ColorChoice::Never => anstream::ColorChoice::Never,
}
}
}