use colored::*;
use std::io;
use std::io::{stderr, stdout, Write};
pub mod prelude {
pub use crate::CargoColor;
pub use crate::CarlogStream;
pub use crate::Status;
}
#[derive(Copy, Clone)]
pub enum CargoColor {
Green,
Cyan,
Yellow,
Red,
White,
Black,
}
impl Default for CargoColor {
fn default() -> Self {
Self::White
}
}
pub enum CarlogStream<'a> {
Stdout,
Stderr,
Custom(&'a mut dyn Write),
}
impl Default for CarlogStream<'_> {
fn default() -> Self {
Self::Stdout
}
}
#[derive(Default)]
pub struct Status {
justify: bool,
bold: bool,
color: CargoColor,
status: String,
}
impl Status {
pub fn new() -> Self {
Self::default()
}
pub fn justify(mut self) -> Self {
self.justify = true;
self
}
pub fn bold(mut self) -> Self {
self.bold = true;
self
}
pub fn color(mut self, color: CargoColor) -> Self {
self.color = color;
self
}
pub fn status<S>(mut self, str: S) -> Self
where
S: AsRef<str>,
{
self.status = str.as_ref().to_string();
self
}
pub fn print_stdout<S>(self, msg: S) -> io::Result<()>
where
S: AsRef<str>,
{
self.print(stdout().lock(), msg)
}
pub fn print_stderr<S>(self, msg: S) -> io::Result<()>
where
S: AsRef<str>,
{
self.print(stderr().lock(), msg)
}
pub fn print<W, S>(self, mut stream: W, msg: S) -> io::Result<()>
where
W: Write,
S: AsRef<str>,
{
let status = Self::color_str(self.color, self.bold, &self.status);
if self.justify {
let padding = " ".repeat(usize::saturating_sub(12, self.status.len()));
write!(stream, "{}{}", padding, status)?;
} else {
write!(stream, "{}", status)?;
}
writeln!(stream, "{}", msg.as_ref())?;
stream.flush()?;
Ok(())
}
fn color_str<S>(color: CargoColor, bold: bool, str: S) -> String
where
S: AsRef<str>,
{
let mut colored = match color {
CargoColor::Green => str.as_ref().green(),
CargoColor::Cyan => str.as_ref().cyan(),
CargoColor::Yellow => str.as_ref().bright_yellow(),
CargoColor::Red => str.as_ref().bright_red(),
CargoColor::White => str.as_ref().white(),
CargoColor::Black => str.as_ref().black(),
};
if bold {
colored = colored.bold()
}
colored.to_string()
}
}
#[macro_export]
macro_rules! carlog {
($status:expr, $message:expr) => {
carlog!($status, $message, crate::CargoColor::default());
};
($status:expr, $message:expr, $color:expr) => {
carlog!(
$status,
$message,
false,
false,
$color,
crate::CarlogStream::default()
)
};
($status:expr, $message:expr, $bold:expr, $justify:expr, $color:expr, $stream:expr) => {
let mut status = crate::Status::new().color($color).status($status);
if $bold {
status = status.bold();
}
if $justify {
status = status.justify();
}
match $stream {
crate::CarlogStream::Stdout => status
.print_stdout($message)
.expect("Failed to print to stdout!"),
crate::CarlogStream::Stderr => status
.print_stderr($message)
.expect("Failed to print to stderr!"),
crate::CarlogStream::Custom(stream) => status
.print(stream, $message)
.expect("Failed to print to custom stream!"),
}
};
}
#[macro_export]
macro_rules! carlog_info {
($status:expr, $message:expr) => {
carlog_info!($status, $message, crate::CarlogStream::default());
};
($status:expr, $message:expr, $stream:expr) => {
carlog!(
$status,
format!(" {}", $message),
true,
true,
crate::CargoColor::Cyan,
$stream
);
};
}
#[macro_export]
macro_rules! carlog_ok {
($status:expr, $message:expr) => {
carlog_ok!($status, $message, crate::CarlogStream::default());
};
($status:expr, $message:expr, $stream:expr) => {
carlog!(
$status,
format!(" {}", $message),
true,
true,
crate::CargoColor::Green,
$stream
);
};
}
#[macro_export]
macro_rules! carlog_warning {
($message:expr) => {
carlog_warning!($message, crate::CarlogStream::default());
};
($message:expr, $stream:expr) => {
carlog!(
"warning",
format!(": {}", $message),
false,
false,
crate::CargoColor::Yellow,
$stream
);
};
}
#[macro_export]
macro_rules! carlog_error {
($message:expr) => {
carlog_error!($message, crate::CarlogStream::default());
};
($message:expr, $stream:expr) => {
carlog!(
"error",
format!(": {}", $message),
false,
false,
crate::CargoColor::Red,
$stream
);
};
}
#[cfg(test)]
mod test {
use crate::CarlogStream;
#[test]
fn test_carlog_info() {
let mut output = Vec::<u8>::new();
carlog_info!(
"Compiling",
"carlog v0.1.0",
CarlogStream::Custom(&mut output)
);
let output = String::from_utf8(output);
assert!(output.is_ok());
assert_eq!(
output.unwrap(),
" \u{1b}[1;36mCompiling\u{1b}[0m carlog v0.1.0\n"
);
}
#[test]
fn test_carlog_ok() {
let mut output = Vec::<u8>::new();
carlog_ok!(
"Compiled",
"carlog v0.1.0",
CarlogStream::Custom(&mut output)
);
let output = String::from_utf8(output);
assert!(output.is_ok());
assert_eq!(
output.unwrap(),
" \u{1b}[1;32mCompiled\u{1b}[0m carlog v0.1.0\n"
);
}
#[test]
fn test_carlog_warning() {
let mut output = Vec::<u8>::new();
carlog_warning!(
"carlog (v0.1.0) generated a warning!",
CarlogStream::Custom(&mut output)
);
let output = String::from_utf8(output);
assert!(output.is_ok());
assert_eq!(
output.unwrap(),
"\u{1b}[93mwarning\u{1b}[0m: carlog (v0.1.0) generated a warning!\n"
);
}
#[test]
fn test_carlog_error() {
let mut output = Vec::<u8>::new();
carlog_error!(
"carlog (v0.1.0) generated an error!",
CarlogStream::Custom(&mut output)
);
let output = String::from_utf8(output);
assert!(output.is_ok());
assert_eq!(
output.unwrap(),
"\u{1b}[91merror\u{1b}[0m: carlog (v0.1.0) generated an error!\n"
);
}
}