use crate::util::error::Error;
use std::ffi::OsStr;
use std::fmt::Display;
use std::fs::File;
use std::io::{ErrorKind, Read, Write};
use std::path::PathBuf;
use std::process;
use termcolor::{Color, ColorSpec, StandardStream, WriteColor};
pub struct CustomWriter<T>
where
T: Fn(String) -> std::io::Result<()>,
{
buf: Vec<u8>,
print_fn: T,
}
impl<T> Write for CustomWriter<T>
where
T: Fn(String) -> std::io::Result<()>,
{
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.buf.append(&mut buf.to_vec());
Result::Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
let res = (self.print_fn)(self.to_string()?);
self.buf = Vec::new();
res
}
}
impl<T> CustomWriter<T>
where
T: Fn(String) -> std::io::Result<()>,
{
pub fn new(func: T) -> CustomWriter<T> {
CustomWriter {
buf: Vec::new(),
print_fn: func,
}
}
pub fn to_string(&self) -> Result<String, Error> {
Ok(String::from_utf8(self.buf.clone())?)
}
}
pub trait ReadLine {
fn read_line_(&mut self) -> Result<String, Error>;
}
pub struct CustomReader {
buf: Vec<String>,
idx: usize,
}
impl ReadLine for std::io::Stdin {
#[cfg_attr(tarpaulin, skip)]
fn read_line_(&mut self) -> Result<String, Error> {
let mut res = String::new();
self.read_line(&mut res)?;
Ok(res)
}
}
impl ReadLine for CustomReader {
fn read_line_(&mut self) -> Result<String, Error> {
if self.buf.len() == self.idx {
Ok(String::from(""))
} else {
let res = self.buf[self.idx].clone();
self.idx += 1;
Ok(res)
}
}
}
pub fn read_line_from(input: &mut impl ReadLine) -> Result<String, Error> {
input.read_line_()
}
impl CustomReader {
pub fn new(s: String) -> CustomReader {
CustomReader {
buf: s.split("\n").map(|x| String::from(x)).collect(),
idx: 0,
}
}
}
pub fn read_file(path: &PathBuf) -> Result<String, Error> {
match path.extension() {
Some(p) => {
if p == OsStr::new("hyeong") {
let mut buf = String::new();
let mut f = File::open(path)?;
f.read_to_string(&mut buf)?;
return Ok(buf);
}
}
_ => {}
}
Err(std::io::Error::new(
ErrorKind::InvalidInput,
"Only .hyeong extension supported",
))?
}
pub fn handle<T>(w: &mut StandardStream, res: Result<T, Error>) -> T {
match res {
Ok(value) => value,
Err(e) => print_error(w, e),
}
}
#[cfg_attr(tarpaulin, skip)]
pub fn print_error(w: &mut StandardStream, err: Error) -> ! {
print_error_no_exit(w, err);
process::exit(1);
}
#[cfg_attr(tarpaulin, skip)]
pub fn print_error_str<S>(w: &mut StandardStream, err: S) -> !
where
S: Display,
{
print_error_str_no_exit(w, err);
process::exit(1);
}
#[cfg_attr(tarpaulin, skip)]
pub fn print_error_no_exit(w: &mut StandardStream, err: Error) {
print_error_str_no_exit(w, err.get_msg());
let note = err.get_note();
if !note.is_empty() {
print_note(w, err.get_note()).unwrap();
}
}
#[cfg_attr(tarpaulin, skip)]
pub fn print_error_str_no_exit<S>(w: &mut StandardStream, err: S)
where
S: Display,
{
write!(w, "[").unwrap();
w.set_color(ColorSpec::new().set_fg(Some(Color::Red)))
.unwrap();
write!(w, "error").unwrap();
w.reset().unwrap();
write!(w, "] ").unwrap();
w.set_color(ColorSpec::new().set_bold(true)).unwrap();
write!(w, "{}", err).unwrap();
w.reset().unwrap();
write!(w, "\n").unwrap();
}
#[cfg_attr(tarpaulin, skip)]
pub fn print_log<S>(w: &mut StandardStream, msg: S) -> Result<(), Error>
where
S: Display,
{
w.set_color(ColorSpec::new().set_fg(Some(Color::Blue)))?;
write!(w, "==>")?;
w.reset()?;
write!(w, " ")?;
w.set_color(ColorSpec::new().set_bold(true))?;
write!(w, "{}", msg)?;
w.reset()?;
write!(w, "\n")?;
Ok(())
}
#[cfg_attr(tarpaulin, skip)]
pub fn print_note<S>(w: &mut StandardStream, msg: S) -> Result<(), Error>
where
S: Display,
{
write!(w, "[")?;
w.set_color(ColorSpec::new().set_fg(Some(Color::Cyan)))?;
write!(w, "note")?;
w.reset()?;
write!(w, "] ")?;
w.set_color(ColorSpec::new().set_bold(true))?;
write!(w, "{}", msg)?;
w.reset()?;
write!(w, "\n")?;
Ok(())
}
pub fn save_to_file(path: &PathBuf, content: String) -> Result<(), Error> {
let mut file = File::create(path)?;
file.write(content.as_bytes())?;
Ok(())
}