From b4c7738d696186d75a61f7fbd32584a1d875e136 Mon Sep 17 00:00:00 2001 From: giulio-Joshi Date: Mon, 10 Oct 2022 22:17:21 +0200 Subject: [PATCH] Modular output methods Using a `std::io::BufWriter` to wrap the output should allow more flexibility. --- src/args.rs | 6 +++--- src/ascii_processor.rs | 46 ++++++++++++++++++++++++------------------ src/main.rs | 42 +++++++------------------------------- src/output.rs | 20 ++++++++++++++++++ 4 files changed, 56 insertions(+), 58 deletions(-) create mode 100644 src/output.rs diff --git a/src/args.rs b/src/args.rs index a787f65..8275382 100644 --- a/src/args.rs +++ b/src/args.rs @@ -1,6 +1,6 @@ pub mod args { - use clap::{Parser, arg, ColorChoice}; use super::enums::*; + use clap::{arg, ColorChoice, Parser}; #[derive(Parser, Debug)] #[command(author, version, about, long_about = None, color = ColorChoice::Always)] @@ -29,7 +29,7 @@ pub mod args { impl Arguments { pub fn validate(&self) -> Result<(), String> { - if self.characters.len() == 0 { + if self.characters.is_empty() { return Err("No characters provided".to_string()); } else if self.characters.len() == 1 { if self.mode == Mode::NormalAscii { @@ -53,7 +53,7 @@ pub mod enums { NormalAscii, /// Colored ASCII art, the colors are based on the terminal colors #[clap(alias = "c")] - COLORED, + Colored, } #[derive(Copy, Clone, ValueEnum, Debug, PartialOrd, Eq, PartialEq)] diff --git a/src/ascii_processor.rs b/src/ascii_processor.rs index 68102c7..4d3e5e1 100644 --- a/src/ascii_processor.rs +++ b/src/ascii_processor.rs @@ -1,41 +1,50 @@ -use image::{GenericImageView, DynamicImage}; +use crate::args::{args::Arguments, enums::Mode}; use colored::{ColoredString, Colorize}; -use crate::args::{ - args::Arguments, - enums::Mode, -}; +use image::{DynamicImage, GenericImageView}; -pub fn generate_ascii(image: DynamicImage, args: &Arguments) -> Vec { +use std::io::{self, BufWriter, Write}; + +pub fn generate_ascii( + image: DynamicImage, + args: &Arguments, + mut buffer: BufWriter, +) -> io::Result<()> { let characters = args.characters.chars().collect::>(); - trace!("Characters: {:?}, length: {}", characters, characters.len()); - let mut output = Vec::new(); let (width, height) = image.dimensions(); for y in 0..height { for x in 0..width { if y % (args.scale * 2) == 0 && x % args.scale == 0 { - output.push(get_character( + let element = get_character( image.get_pixel(x, y), - &characters, args.mode, + &characters, + args.mode, &args.background, - )); + ); + + buffer.write_all(format!("{element}").as_bytes())?; } } // Add a new line at the end of each row if y % (args.scale * 2) == 0 { - output.push("\n".into()); + buffer.write_all("\n".as_bytes())?; } } - output + Ok(()) } fn get_character( pixel: image::Rgba, - characters: &Vec, mode: Mode, + characters: &Vec, + mode: Mode, background: &Option, ) -> ColoredString { - let intent = if pixel[3] == 0 { 0 } else { pixel[0] / 3 + pixel[1] / 3 + pixel[2] / 3 }; + let intent = if pixel[3] == 0 { + 0 + } else { + pixel[0] / 3 + pixel[1] / 3 + pixel[2] / 3 + }; let ch = characters[(intent / (32 + 7 - (7 + (characters.len() - 7)) as u8)) as usize]; @@ -43,14 +52,11 @@ fn get_character( let ch = match mode { Mode::NormalAscii => ColoredString::from(&*ch), - Mode::COLORED => { - ch.to_string() - .truecolor(pixel[0], pixel[1], pixel[2]) - } + Mode::Colored => ch.truecolor(pixel[0], pixel[1], pixel[2]), }; match background { Some(bg) => ch.on_color(bg.to_string()), - None => ch + None => ch, } } diff --git a/src/main.rs b/src/main.rs index e5ab49c..f600fcb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,23 +1,22 @@ -use std::io::Write; use clap::Parser; use image::GenericImageView; +use std::io::Result; extern crate pretty_env_logger; + #[macro_use] extern crate log; mod args; mod ascii_processor; +mod output; -use crate::args::{ - args::Arguments, - enums::OutputMethod, -}; +use crate::args::{args::Arguments, enums::OutputMethod}; use crate::ascii_processor::generate_ascii; -fn main() { +fn main() -> Result<()> { // Initialize the logger pretty_env_logger::init(); info!("Successfully initialized logger"); @@ -51,34 +50,7 @@ fn main() { info!("Successfully opened image"); trace!("Image dimensions: {:?}", image.dimensions()); - // Process the image - let output = generate_ascii(image, &arguments); + generate_ascii(image, &arguments, output::prepare_output(&arguments)?)?; info!("Successfully processed image"); - - // Output the image - info!("Outputting image"); - match arguments.output_method { - OutputMethod::File => { - match std::fs::write( - arguments.output.clone(), - output.iter() - .map(|s| format!("{}", s)) - .collect::(), - ) { - Ok(_) => info!("Successfully outputted image: {}", arguments.output), - Err(e) => { - error!("Failed to output image: {:?}", e); - eprintln!("Failed to output image: {:?}", e); - std::process::exit(1); - } - } - } - OutputMethod::Stdout => { - for char in output { - print!("{}", char); - std::io::stdout().flush().unwrap(); - } - info!("Successfully outputted image"); - } - } + Ok(()) } diff --git a/src/output.rs b/src/output.rs new file mode 100644 index 0000000..9a169fe --- /dev/null +++ b/src/output.rs @@ -0,0 +1,20 @@ +use std::fs::File; +use std::io; +use std::io::BufWriter; +use std::io::Write; + +use super::Arguments; +use super::OutputMethod; + +pub fn prepare_output(arguments: &Arguments) -> io::Result>> { + match arguments.output_method { + OutputMethod::File => { + let output_file = Box::new(File::create(&arguments.output)?); + Ok(BufWriter::with_capacity(1024, output_file)) + } + OutputMethod::Stdout => { + let output_wrap = Box::new(std::io::stdout().lock()); + Ok(BufWriter::with_capacity(1024, output_wrap)) + } + } +}