use std::fs::File;
use std::io::{self, IsTerminal, Write};
use malevich::pixel::Capabilities;
use malevich::{Frame, Plot};
use crate::args::{Args, CharsetChoice, ColorChoice, Output, PixelsChoice};
use crate::chart::Built;
#[derive(Debug)]
pub enum EmitError {
Io(io::Error),
Render(malevich::Error),
}
impl From<io::Error> for EmitError {
fn from(error: io::Error) -> EmitError {
EmitError::Io(error)
}
}
impl From<malevich::Error> for EmitError {
fn from(error: malevich::Error) -> EmitError {
EmitError::Render(error)
}
}
pub fn apply_color(choice: ColorChoice) {
match choice {
ColorChoice::Never => unsafe { std::env::set_var("NO_COLOR", "1") },
ColorChoice::Always => unsafe {
std::env::remove_var("NO_COLOR");
std::env::set_var("CLICOLOR_FORCE", "1");
},
ColorChoice::Auto => {}
}
}
pub fn emit(args: &Args, built: &Built<'_>) -> Result<i32, EmitError> {
match &args.output {
Output::Stderr => plot_to(io::stderr(), &built.plot, args)?,
Output::Stdout => plot_to(io::stdout(), &built.plot, args)?,
Output::File(path) => {
let file = File::create(path)?;
plot_to(file, &built.plot, args)?;
}
}
if built.unparsed > 0 && !args.quiet {
let noun = if built.unparsed == 1 {
"value"
} else {
"values"
};
eprintln!("{} {noun} could not be parsed", built.unparsed);
}
Ok(0)
}
fn plot_to<W: Write + IsTerminal>(
mut dest: W,
plot: &Plot<'_>,
args: &Args,
) -> Result<(), EmitError> {
let frame = frame_for(&dest, args);
let text = render(plot, &frame, args.pixels, &dest)?;
dest.write_all(text.as_bytes())?;
dest.write_all(b"\n")?;
Ok(dest.flush()?)
}
pub(crate) fn frame_for<T: IsTerminal>(dest: &T, args: &Args) -> Frame {
let mut frame = Frame::detect_for(dest);
if let CharsetChoice::Fixed(charset) = args.charset {
frame.charset = charset;
}
if let Some(width) = args.width {
frame.width = width;
}
if let Some(height) = args.height {
frame.height = height;
}
frame
}
fn render<T: IsTerminal>(
plot: &Plot<'_>,
frame: &Frame,
pixels: PixelsChoice,
destination: &T,
) -> malevich::Result<String> {
match pixels {
PixelsChoice::Never => plot.try_render(frame),
PixelsChoice::Auto if destination.is_terminal() => {
plot.try_render_with_capabilities(frame, &Capabilities::detect_for(destination))
}
PixelsChoice::Auto => plot.try_render(frame),
PixelsChoice::Always => {
plot.try_render_with_capabilities(frame, &Capabilities::detect_for(destination))
}
}
}