use std::fmt;
use std::io::Write;
#[cfg(unix)]
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
use std::str;
use progname;
#[macro_export]
macro_rules! err {
($status:expr, $fmt:expr) => (
$crate::err::verrp($status, None as Option<&str>,
format_args!(concat!($fmt, "\n")));
);
($status:expr, $fmt:expr, $($args:tt)*) => (
$crate::err::verrp($status, None as Option<&str>,
format_args!(concat!($fmt, "\n"), $($args)*));
);
}
#[macro_export]
macro_rules! errp {
($status:expr, $path:expr, $fmt:expr) => (
$crate::err::verrp($status, Some($path),
format_args!(concat!($fmt, "\n")));
);
($status:expr, $path:expr, $fmt:expr, $($args:tt)*) => (
$crate::err::verrp($status, Some($path),
format_args!(concat!($fmt, "\n"), $($args)*));
);
}
#[macro_export]
macro_rules! warn {
($fmt:expr) => (
$crate::err::vwarnp(None as Option<&str>,
format_args!(concat!($fmt, "\n")));
);
($fmt:expr, $($args:tt)*) => (
$crate::err::vwarnp(None as Option<&str>,
format_args!(concat!($fmt, "\n"), $($args)*));
);
}
#[macro_export]
macro_rules! warnp {
($path:expr, $fmt:expr) => (
$crate::err::vwarnp(Some($path),
format_args!(concat!($fmt, "\n")));
);
($path:expr, $fmt:expr, $($args:tt)*) => (
$crate::err::vwarnp(Some($path),
format_args!(concat!($fmt, "\n"), $($args)*));
);
}
pub fn verrp<P>(status: i32, path: Option<P>, fmt: fmt::Arguments) -> ! where P: AsRef<Path> {
vwarnp(path, fmt);
tester::exit(status);
}
pub fn vwarnp<P>(path: Option<P>, fmt: fmt::Arguments) where P: AsRef<Path> {
let mut buf = Vec::new();
if let Some(ref os) = *progname::getprogname_arc() {
#[cfg(unix)]
buf.extend_from_slice(os.as_bytes());
#[cfg(not(unix))]
match os.to_str() {
Some(s) => { let _ = write!(&mut buf, "{}", s); },
None => {},
};
}
buf.extend_from_slice(b": ");
if let Some(path) = path {
#[cfg(unix)]
buf.extend_from_slice(path.as_ref().as_os_str().as_bytes());
#[cfg(not(unix))]
match path.as_ref().to_str() {
Some(s) => { let _ = write!(&mut buf, "{}", s); },
None => {},
};
buf.extend_from_slice(b": ");
}
let msgstart = buf.len();
let _ = buf.write_fmt(fmt);
if let Err(e) = tester::stderr().write(&buf) {
let msg = str::from_utf8(&buf[msgstart..]).unwrap_or("");
panic!("failed to write to stderr: {}: {}", e, msg);
}
}
#[cfg(not(test))]
mod tester {
#[inline(always)]
pub fn exit(status: i32) -> ! { ::std::process::exit(status); }
#[inline(always)]
pub fn stderr() -> ::std::io::Stderr { ::std::io::stderr() }
}
#[cfg(test)]
mod tester {
pub fn exit(status: i32) -> ! { panic!("expected exit with {}", status); }
pub fn stderr() -> DummyStderr { DummyStderr::new() }
use std::cell::RefCell;
use std::io;
use std::io::Result;
thread_local! {
static STDERR_BUF: RefCell<Vec<u8>> = RefCell::new(Vec::new());
}
pub struct DummyStderr();
impl DummyStderr {
pub fn new() -> DummyStderr { DummyStderr() }
}
impl io::Write for DummyStderr {
fn write(&mut self, buf: &[u8]) -> Result<usize> {
STDERR_BUF.with(|v| v.borrow_mut().extend_from_slice(buf));
Ok(buf.len())
}
fn flush(&mut self) -> Result<()> { Ok(()) }
}
pub fn get_stderr() -> Vec<u8> {
STDERR_BUF.with(|v| v.borrow().clone())
}
}
#[cfg(test)]
mod tests {
use std::ffi::OsStr;
use super::*;
#[test]
#[should_panic(expected = "expected exit with 0")]
fn err1() {
err!(0, "err 1");
}
#[test]
#[should_panic(expected = "expected exit with 9")]
fn err2() {
err!(9, "err {}", 2);
}
#[test]
#[should_panic(expected = "expected exit with 0")]
fn errp3() {
errp!(0, Path::new("Path"), "{} {}", "errp", 3);
}
#[test]
fn warn() {
warn!("warn 1");
assert!(tester::get_stderr().ends_with(b": warn 1\n"));
warn!("warn {}", 2);
assert!(tester::get_stderr().ends_with(b": warn 2\n"));
warn!("{} {}", "warn", 3);
assert!(tester::get_stderr().ends_with(b": warn 3\n"));
}
#[test]
fn warnp() {
warnp!("str", "warnp 1");
assert!(tester::get_stderr().ends_with(b": str: warnp 1\n"));
warnp!("str", "warnp {}", 2);
assert!(tester::get_stderr().ends_with(b": str: warnp 2\n"));
warnp!("str", "{} {}", "warnp", 3);
assert!(tester::get_stderr().ends_with(b": str: warnp 3\n"));
warnp!(Path::new("Path"), "warnp 1");
assert!(tester::get_stderr().ends_with(b": Path: warnp 1\n"));
warnp!(OsStr::new("OsStr"), "warnp 1");
assert!(tester::get_stderr().ends_with(b": OsStr: warnp 1\n"));
}
#[test]
#[should_panic(expected = "expected exit with 1")]
fn err_named_param() {
err!(1, "x = {x}, y = {y}", y = 20, x = 10);
}
#[test]
fn warn_named_param() {
warn!("x = {x}, y = {y}", y = 20, x = 10);
assert!(tester::get_stderr().ends_with(b": x = 10, y = 20\n"));
}
}