use std::env;
use std::io::{self, Write};
use std::str::FromStr;
use std::thread;
use std::time::Duration;
use aisling::{Effect, EffectConfig, EffectKind};
fn main() {
let args = Args::parse();
let kind = EffectKind::from_str(&args.effect).unwrap_or(EffectKind::Wipe);
let mut config = EffectConfig::default()
.with_duration(args.duration)
.with_seed(args.seed);
if let (Some(width), Some(height)) = (args.width, args.height) {
config = config.with_canvas_size(width.max(1), height.max(1));
}
let effect = Effect::with_config(kind, args.text, config);
let mut stdout = io::stdout();
for frame in effect.iter() {
write!(stdout, "\x1b[2J\x1b[H{}", frame.to_ansi_string()).unwrap();
stdout.flush().unwrap();
thread::sleep(Duration::from_millis((1000 / args.fps.max(1)) as u64));
}
writeln!(stdout).unwrap();
}
struct Args {
effect: String,
text: String,
width: Option<usize>,
height: Option<usize>,
fps: u64,
duration: usize,
seed: u64,
}
impl Args {
fn parse() -> Self {
let mut width = None;
let mut height = None;
let mut fps = 24u64;
let mut duration = None;
let mut seconds = None;
let mut seed = 7u64;
let mut text = None;
let mut positional: Vec<String> = Vec::new();
let mut args = env::args().skip(1).peekable();
while let Some(arg) = args.next() {
match arg.as_str() {
"--width" => {
width = args.next().and_then(|value| value.parse::<usize>().ok());
}
"--height" => {
height = args.next().and_then(|value| value.parse::<usize>().ok());
}
"--fps" => {
fps = args
.next()
.and_then(|value| value.parse::<u64>().ok())
.unwrap_or(fps);
}
"--duration" => {
duration = args
.next()
.and_then(|value| value.parse::<usize>().ok())
.map(|value| value.max(1));
}
"--seconds" => {
seconds = args
.next()
.and_then(|value| value.parse::<u64>().ok())
.map(|value| value.max(1));
}
"--seed" => {
seed = args
.next()
.and_then(|value| value.parse::<u64>().ok())
.unwrap_or(seed);
}
"--text" => {
text = args.next();
}
"-h" | "--help" | "help" => {
usage();
std::process::exit(0);
}
_ if !arg.starts_with('-') => {
positional.push(arg);
}
_ => {
eprintln!("Ignoring unknown argument: {arg}");
}
}
}
let effect = positional
.first()
.cloned()
.unwrap_or_else(|| "wipe".to_string());
let text = text
.or_else(|| positional.get(1).cloned())
.unwrap_or_else(|| "Aisling".to_string());
let duration = duration
.or_else(|| seconds.map(|value| (value as usize).saturating_mul(fps as usize)))
.unwrap_or(90);
Self {
effect,
text,
width,
height,
fps,
duration,
seed,
}
}
}
fn usage() {
println!("Usage:");
println!(" cargo run --example demo [effect] [text]");
println!(" cargo run --example demo -- [effect] [text] --width <cols> --height <rows> --fps <n> --duration <frames>");
println!(" cargo run --example demo -- [effect] --text <text> --seconds <seconds> --fps <n>");
println!();
println!("Examples:");
println!(" cargo run --example demo -- wipe \"Aisling\"");
println!(" cargo run --example demo -- matrix \"Hello\" --width 120 --height 30 --fps 24");
}