1use std::fmt;
30use std::io::Write;
31#[cfg(unix)]
32use std::os::unix::ffi::OsStrExt;
33use std::path::Path;
34use std::str;
35
36use progname;
37
38#[macro_export]
43macro_rules! err {
44 ($status:expr, $fmt:expr) => (
45 $crate::err::verrp($status, None as Option<&str>,
46 format_args!(concat!($fmt, "\n")));
47 );
48 ($status:expr, $fmt:expr, $($args:tt)*) => (
49 $crate::err::verrp($status, None as Option<&str>,
50 format_args!(concat!($fmt, "\n"), $($args)*));
51 );
52}
53
54#[macro_export]
59macro_rules! errp {
60 ($status:expr, $path:expr, $fmt:expr) => (
61 $crate::err::verrp($status, Some($path),
62 format_args!(concat!($fmt, "\n")));
63 );
64 ($status:expr, $path:expr, $fmt:expr, $($args:tt)*) => (
65 $crate::err::verrp($status, Some($path),
66 format_args!(concat!($fmt, "\n"), $($args)*));
67 );
68}
69
70#[macro_export]
74macro_rules! warn {
75 ($fmt:expr) => (
76 $crate::err::vwarnp(None as Option<&str>,
77 format_args!(concat!($fmt, "\n")));
78 );
79 ($fmt:expr, $($args:tt)*) => (
80 $crate::err::vwarnp(None as Option<&str>,
81 format_args!(concat!($fmt, "\n"), $($args)*));
82 );
83}
84
85#[macro_export]
89macro_rules! warnp {
90 ($path:expr, $fmt:expr) => (
91 $crate::err::vwarnp(Some($path),
92 format_args!(concat!($fmt, "\n")));
93 );
94 ($path:expr, $fmt:expr, $($args:tt)*) => (
95 $crate::err::vwarnp(Some($path),
96 format_args!(concat!($fmt, "\n"), $($args)*));
97 );
98}
99
100pub fn verrp<P>(status: i32, path: Option<P>, fmt: fmt::Arguments) -> ! where P: AsRef<Path> {
103 vwarnp(path, fmt);
104 tester::exit(status);
105}
106
107pub fn vwarnp<P>(path: Option<P>, fmt: fmt::Arguments) where P: AsRef<Path> {
110 let mut buf = Vec::new();
111 if let Some(ref os) = *progname::getprogname_arc() {
112 #[cfg(unix)]
113 buf.extend_from_slice(os.as_bytes());
114 #[cfg(not(unix))]
115 match os.to_str() {
116 Some(s) => { let _ = write!(&mut buf, "{}", s); },
117 None => {},
118 };
119 }
120 buf.extend_from_slice(b": ");
121 if let Some(path) = path {
122 #[cfg(unix)]
123 buf.extend_from_slice(path.as_ref().as_os_str().as_bytes());
124 #[cfg(not(unix))]
125 match path.as_ref().to_str() {
126 Some(s) => { let _ = write!(&mut buf, "{}", s); },
127 None => {},
128 };
129 buf.extend_from_slice(b": ");
130 }
131 let msgstart = buf.len();
132 let _ = buf.write_fmt(fmt);
133 if let Err(e) = tester::stderr().write(&buf) {
134 let msg = str::from_utf8(&buf[msgstart..]).unwrap_or("");
136 panic!("failed to write to stderr: {}: {}", e, msg);
139 }
140}
141
142#[cfg(not(test))]
143mod tester {
144 #[inline(always)]
145 pub fn exit(status: i32) -> ! { ::std::process::exit(status); }
146 #[inline(always)]
147 pub fn stderr() -> ::std::io::Stderr { ::std::io::stderr() }
148}
149
150#[cfg(test)]
151mod tester {
152 pub fn exit(status: i32) -> ! { panic!("expected exit with {}", status); }
153 pub fn stderr() -> DummyStderr { DummyStderr::new() }
154
155 use std::cell::RefCell;
156 use std::io;
157 use std::io::Result;
158 thread_local! {
159 static STDERR_BUF: RefCell<Vec<u8>> = RefCell::new(Vec::new());
160 }
161 pub struct DummyStderr();
162 impl DummyStderr {
163 pub fn new() -> DummyStderr { DummyStderr() }
164 }
165 impl io::Write for DummyStderr {
166 fn write(&mut self, buf: &[u8]) -> Result<usize> {
167 STDERR_BUF.with(|v| v.borrow_mut().extend_from_slice(buf));
168 Ok(buf.len())
169 }
170 fn flush(&mut self) -> Result<()> { Ok(()) }
171 }
172 pub fn get_stderr() -> Vec<u8> {
173 STDERR_BUF.with(|v| v.borrow().clone())
174 }
175}
176
177#[cfg(test)]
178mod tests {
179 use std::ffi::OsStr;
180 use super::*;
181
182 #[test]
187 #[should_panic(expected = "expected exit with 0")]
188 fn err1() {
189 err!(0, "err 1");
190 }
191
192 #[test]
193 #[should_panic(expected = "expected exit with 9")]
194 fn err2() {
195 err!(9, "err {}", 2);
196 }
197
198 #[test]
199 #[should_panic(expected = "expected exit with 0")]
200 fn errp3() {
201 errp!(0, Path::new("Path"), "{} {}", "errp", 3);
202 }
203
204 #[test]
205 fn warn() {
206 warn!("warn 1");
207 assert!(tester::get_stderr().ends_with(b": warn 1\n"));
208 warn!("warn {}", 2);
209 assert!(tester::get_stderr().ends_with(b": warn 2\n"));
210 warn!("{} {}", "warn", 3);
211 assert!(tester::get_stderr().ends_with(b": warn 3\n"));
212 }
213
214 #[test]
215 fn warnp() {
216 warnp!("str", "warnp 1");
217 assert!(tester::get_stderr().ends_with(b": str: warnp 1\n"));
218 warnp!("str", "warnp {}", 2);
219 assert!(tester::get_stderr().ends_with(b": str: warnp 2\n"));
220 warnp!("str", "{} {}", "warnp", 3);
221 assert!(tester::get_stderr().ends_with(b": str: warnp 3\n"));
222 warnp!(Path::new("Path"), "warnp 1");
223 assert!(tester::get_stderr().ends_with(b": Path: warnp 1\n"));
224 warnp!(OsStr::new("OsStr"), "warnp 1");
225 assert!(tester::get_stderr().ends_with(b": OsStr: warnp 1\n"));
226 }
227
228 #[test]
229 #[should_panic(expected = "expected exit with 1")]
230 fn err_named_param() {
231 err!(1, "x = {x}, y = {y}", y = 20, x = 10);
232 }
233
234 #[test]
235 fn warn_named_param() {
236 warn!("x = {x}, y = {y}", y = 20, x = 10);
237 assert!(tester::get_stderr().ends_with(b": x = 10, y = 20\n"));
238 }
239}