userror/lib.rs
1//! Some basic functions and macros for printing user-facing errors in command-line programs.
2//!
3//! # Installation
4//!
5//! Simply mark userror as a dependency in your Cargo.toml
6//!
7//! ```toml
8//! [dependencies]
9//! userror = "0.1.0"
10//! ```
11//!
12//! By default userror prints coloured messages with ansi_term, if you do not want this use
13//! `default-features = false`
14//!
15//! ```toml
16//! [dependencies]
17//! userror = { version = "0.1.0", default-features = false }
18//! ```
19
20#[cfg(feature = "colour")]
21extern crate ansi_term;
22
23#[cfg(feature = "colour")]
24use ansi_term::Colour;
25
26#[cfg(not(feature = "colour"))]
27enum Colour {
28 Purple,
29 Blue,
30 Yellow,
31 Red,
32}
33
34#[cfg(not(feature = "colour"))]
35impl Colour {
36 fn paint<'l>(&self, level: &'l str) -> &'l str {
37 level
38 }
39}
40
41
42use std::io::{self, Write};
43
44/// Prepend file and line info into a given message.
45///
46/// This is useful for internal errors to avoid messy uses of `file!` and `line!`.
47///
48/// The first argument to this macro must always be a string literal. If a single argument is
49/// given then a `&'static str` will be returned. If multiple arguments are given the the first
50/// argument is used as a format string for `format!`.
51#[macro_export]
52macro_rules! flm {
53 () => (concat!(file!(), ":", line!()));
54
55 ($message:expr) => (concat!(file!(), ":", line!(), ": ", $message));
56
57 ($format:expr, $( $val:expr ),+) => (
58 format!(concat!(file!(), ":", line!(), ": ", $format), $( $val ),+)
59 );
60}
61
62/// Prepend file and line info into a call to `.expect()`.
63#[macro_export]
64macro_rules! expect {
65 ($value:expr) => ($value.expect(flm!()));
66
67 ($value:expr, $message:expr) => ($value.expect(flm!($message)));
68}
69
70/// Display an internal error message with file and line info.
71///
72/// Internal errors are bugs or failed invariants in your program, hence file and line info are
73/// useful for debugging.
74#[macro_export]
75macro_rules! internal {
76 ($message:expr) => ($crate::internal(flm!($message)));
77
78 ($format:expr, $( $val:expr ),+) => ($crate::internal(&flm!($format, $( $val ),+)));
79}
80
81fn print(colour: Colour, level: &str, message: &str) -> io::Result<()> {
82 let program = try!(std::env::current_exe());
83 let program = program.file_name().and_then(|n| n.to_str());
84 match program {
85 Some(name) => writeln!(
86 io::stderr(),
87 "{}:{}: {}",
88 Colour::Blue.paint(name),
89 colour.paint(level),
90 message,
91 ),
92 None => writeln!(io::stderr(), "{}: {}", colour.paint(level), message),
93 }
94}
95
96/// Print an internal error message.
97///
98/// Internal errors are bugs or failed invariants in your program. They are not necessarily fatal.
99pub fn internal(message: &str) -> io::Result<()> {
100 print(Colour::Red, "internal", message)
101}
102
103/// Print a fatal error message and panic.
104///
105/// Fatal errors are errors which can not be recovered from, such as failing to receive user input.
106pub fn fatal(message: &str) -> ! {
107 print(Colour::Red, "fatal", message).expect("failed to write error message");
108 panic!("fatal error occurred");
109}
110
111/// Print an error message.
112///
113/// Errors are recoverable but prevent the program from working properly or in it's entirety, such
114/// as failing to open an output file and instead printing results to screen.
115pub fn error(message: &str) -> io::Result<()> {
116 print(Colour::Red, "error", message)
117}
118
119/// Print a warning message.
120///
121/// Warnings lead to sub-optimal, but not strictly incorrect, behaviour. An example would be
122/// failing to load a custom stylesheet and instead using a default one.
123pub fn warn(message: &str) -> io::Result<()> {
124 print(Colour::Yellow, "warning", message)
125}
126
127/// Print some non-erroneous information.
128pub fn info(message: &str) -> io::Result<()> {
129 print(Colour::Purple, "info", message)
130}