cargo-equip 0.13.0

A Cargo subcommand to bundle your code into one `.rs` file for competitive programming.
Documentation
use std::{
    fmt,
    io::{self, Write},
};
use termcolor::{Color, ColorSpec, StandardStream, WriteColor as _};

pub struct Shell {
    output: ShellOut,
}

impl Shell {
    pub fn new() -> Self {
        Self {
            output: ShellOut::stream(),
        }
    }

    pub(crate) fn out(&mut self) -> &mut dyn Write {
        let ShellOut::Stream { stdout, .. } = &mut self.output;
        stdout
    }

    pub fn err(&mut self) -> &mut dyn Write {
        let ShellOut::Stream { stderr, .. } = &mut self.output;
        stderr
    }

    pub(crate) fn status(
        &mut self,
        status: impl fmt::Display,
        message: impl fmt::Display,
    ) -> io::Result<()> {
        self.print(status, message, Color::Green, true)
    }

    pub(crate) fn warn(&mut self, message: impl fmt::Display) -> io::Result<()> {
        self.print("warning", message, Color::Yellow, false)
    }

    pub fn error(&mut self, message: impl fmt::Display) -> io::Result<()> {
        self.print("error", message, Color::Red, false)
    }

    fn print(
        &mut self,
        status: impl fmt::Display,
        message: impl fmt::Display,
        color: Color,
        justified: bool,
    ) -> io::Result<()> {
        let ShellOut::Stream { stderr, .. } = &mut self.output;
        stderr.set_color(ColorSpec::new().set_bold(true).set_fg(Some(color)))?;
        if justified {
            write!(stderr, "{:>12}", status)?;
        } else {
            write!(stderr, "{}", status)?;
            stderr.set_color(ColorSpec::new().set_bold(true))?;
            write!(stderr, ":")?;
        }
        stderr.reset()?;
        writeln!(stderr, " {}", message)
    }
}

impl Default for Shell {
    fn default() -> Self {
        Self::new()
    }
}

enum ShellOut {
    Stream {
        stdout: StandardStream,
        stderr: StandardStream,
    },
}

impl ShellOut {
    fn stream() -> Self {
        Self::Stream {
            stdout: StandardStream::stdout(if atty::is(atty::Stream::Stdout) {
                termcolor::ColorChoice::Auto
            } else {
                termcolor::ColorChoice::Never
            }),
            stderr: StandardStream::stderr(if atty::is(atty::Stream::Stderr) {
                termcolor::ColorChoice::Auto
            } else {
                termcolor::ColorChoice::Never
            }),
        }
    }
}