diff options
author | Arjun Satarkar <me@arjunsatarkar.net> | 2023-06-01 19:07:04 +0000 |
---|---|---|
committer | Arjun Satarkar <me@arjunsatarkar.net> | 2023-06-01 19:07:04 +0000 |
commit | eecb04dcc4348af5fd9161c4ee92d59f23885924 (patch) | |
tree | caddab9b8d38be14edb74d63b6ad7d585dad13bd /hitomezashi_cli/src/main.rs | |
parent | 3a2fef94230f35d948634ffef83660afdffa0bad (diff) | |
download | hitomezashi-eecb04dcc4348af5fd9161c4ee92d59f23885924.tar hitomezashi-eecb04dcc4348af5fd9161c4ee92d59f23885924.tar.gz hitomezashi-eecb04dcc4348af5fd9161c4ee92d59f23885924.zip |
Add improved CLI (written in Rust)
Diffstat (limited to 'hitomezashi_cli/src/main.rs')
-rw-r--r-- | hitomezashi_cli/src/main.rs | 69 |
1 files changed, 69 insertions, 0 deletions
diff --git a/hitomezashi_cli/src/main.rs b/hitomezashi_cli/src/main.rs new file mode 100644 index 0000000..3f27a72 --- /dev/null +++ b/hitomezashi_cli/src/main.rs @@ -0,0 +1,69 @@ +use bitvec::prelude::*; +use clap::Parser; +use libhitomezashi as hitomezashi; +use std::process::ExitCode; + +#[derive(Parser, Debug)] +#[command(author, version, about)] +struct Args { + #[arg(short = 'x', long, value_parser = validate_xy_pattern)] + x_pattern: BitVec, + #[arg(short = 'y', long, value_parser = validate_xy_pattern)] + y_pattern: BitVec, + #[arg(short = 'g', long)] + gap: usize, + #[arg(short = 'l', long)] + line_thickness: usize, + #[arg(short = 'f', long, value_parser = parse_rgb_str)] + foreground_colour: image::Rgb<u8>, + #[arg(short = 'b', long, value_parser = parse_rgb_str)] + background_colour: image::Rgb<u8>, + #[arg(short = 'o', long)] + output_path: String, +} + +fn validate_xy_pattern(pattern_raw: &str) -> Result<BitVec, String> { + let mut result = BitVec::new(); + for ch in pattern_raw.chars() { + if ch == '1' { + result.push(true); + } else if ch == '0' { + result.push(false); + } else { + return Err(String::from("must consist of only 0 and 1")); + } + } + Ok(result) +} + +fn parse_rgb_str(rgb_str: &str) -> Result<image::Rgb<u8>, String> { + let rgb: csscolorparser::Color = match csscolorparser::parse(rgb_str) { + Ok(rgb) => rgb, + Err(e) => return Err(e.to_string()) + }; + Ok(image::Rgb::from([(rgb.r * u8::MAX as f64) as u8, (rgb.g * u8::MAX as f64) as u8, (rgb.b * u8::MAX as f64) as u8])) +} + +fn main() -> ExitCode { + let args = Args::parse(); + + let pattern = hitomezashi::get_hitomezashi_pattern( + args.x_pattern, + args.y_pattern, + args.gap, + args.line_thickness, + ); + let mut image = image::RgbImage::new(pattern.width as u32, pattern.height as u32); + for x in 0..pattern.width { + for y in 0..pattern.height { + image.put_pixel(x as u32, y as u32, if pattern[x][y] {args.foreground_colour} else {args.background_colour}) + } + } + match image.save_with_format(&args.output_path, image::ImageFormat::Png) { + Ok(_) => ExitCode::SUCCESS, + Err(e) => { + eprintln!("error: failed to save output image to {}: {}", args.output_path, e); + ExitCode::FAILURE + } + } +} |