1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
//! Wrapper crate for [`termcolor_output_impl`] procedural macro.
//!
//! The reason for this code to be split into two crates is simple: we want to make
//! this functionality available on stable. In fact, this dual-crate system is simply
//! the manual implementation of the code generated by [`proc_macro_hack`].
//!
//! ## What is it
//!
//! The [`termcolor`] crate is a cross-platform implementation for the different console
//! APIs, abstracting away both Linux terminals and Windows consoles. It has, however,
//! a but cumbersome API itself (only a bit though), since for formatting-heavy parts
//! we have to litter our code with explicit styling commands. This crate allows to
//! abstract these things away, providing the interface similar to the standard [`write!`]
//! macro.
//!  
//! [`termcolor_output_impl`]: http://crates.io/crates/termcolor_output_impl
//! [`proc_macro_hack`]: http://github.com/dtolnay/proc-macro-hack
//! [`termcolor`]: http://docs.rs/termcolor
//! [`write!`]: https://doc.rust-lang.org/stable/std/macro.write.html

trait ColoredOutput {}

mod seal {
    pub trait Seal {}
    impl<T: termcolor::WriteColor> Seal for T {}
}

/// Extension trait for [`WriteColor`] instances.
///
/// This trait is not intended for public use. Its only purpose is to allow us check if the
/// provided value implements [`WriteColor`] in an ergonomic way, without consuming the value if
/// unnecessary, and it is used internally by the [`colored`] macro.
///
/// You'll probably see this trait only in type errors, if the first argument to the macro appears
/// to be of the wrong type.
///
/// ## Example
///
/// ```compile_fail
/// use termcolor_output::colored;
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// colored!(0u8, "This won't be written")?;
/// # Ok(())
/// # }
/// ```
///
/// This example yields the following error:
/// ```text
/// error[E0599]: no method named `guard` found for type `u8` in the current scope
///  --> src/lib.rs:37:1
///    |
///  5 | colored!(0u8, "This won't be written")?;
///    |          ^^^                          
///    |
///    = note: the method `guard` exists but the following trait bounds were not satisfied:
///                    `u8 : termcolor_output::WriteColorGuard`
/// ```
///
/// This trait is implemented on everything that implements `WriteColor` and sealed, so that it
/// can't be implemented elsewhere.
///
/// [`WriteColor`]: https://docs.rs/termcolor/1.0.5/termcolor/trait.WriteColor.html
/// [`colored`]: colored
pub trait WriteColorGuard: seal::Seal {
    fn guard(&mut self) -> &mut Self {
        self
    }
}
impl<T: termcolor::WriteColor> WriteColorGuard for T {}

#[doc(hidden)]
pub use std;
#[doc(hidden)]
pub use termcolor;
#[doc(hidden)]
pub use termcolor_output_impl;

/// The macro writing colored text.
///
/// Like the standard [`write!`] macro, it takes the writer, format string and the sequence of
/// arguments. The arguments may be either formattable with the corresponding formatter (`Display`
/// for `{}`, `Debug` for `{:?}`, etc.), or the _control sequences_, which are written in
/// macro-like style:
/// - `reset!()` yields call to [`ColorSpec::clear`][termcolor::ColorSpec::clear];
/// - `fg!(color)`, `bg!(color)`, `bold!(bool)`, `underline!(bool)` and `intense!(bool)` are
/// translated into corresponding `ColorSpec::set_*` calls with the provided arguments.
///
/// Internally, this expands to the following:
/// - imports of all necessary traits;
/// - call to the `guard` method on the [`WriteColorGuard`] trait (as a sanity check);
/// - an immediately called closure, containing:
///   - creation of `ColorSpec`;
///   - calls to `write!` for every formattable input;
///   - updates for `ColorSpec` for every control sequence.
/// Every error generated inside the closure is returned early and yielded by the macro as an
/// [`std::io::Result<()>`].
///
/// When the arguments list is malformed, macro generates a compile error trying to point on the
/// exact origin.
///
/// ## Examples
///
/// Simple formatting is provided in exactly the same way as for standard writes:
/// ```
/// # use termcolor_output::colored;
/// # fn write(writer: &mut impl termcolor::WriteColor) {
/// colored!(writer, "This text is {} styled", "not").unwrap();
/// # }
/// ```
///
/// Styled formatting is provided by using any formatter argument in format string, wherever you
/// need to apply the style:
/// ```
/// # use termcolor_output::colored;
/// # fn write(writer: &mut impl termcolor::WriteColor) {
/// # use termcolor::Color;
/// colored!(writer, "This text is not styled\n{}And this is colored", fg!(Some(Color::Blue))).unwrap();
/// # }
/// ```
///
/// You can chain several styling commands by specifying several formatter arguments without text
/// between them:
/// ```
/// # use termcolor_output::colored;
/// # fn write(writer: &mut impl termcolor::WriteColor) {
/// # use termcolor::Color;
/// colored!(
///     writer,
///     "{}{}{}This text is bold blue on yellow background
///      {}{}{}And this has default colors, but is bold and underlined",
///     fg!(Some(Color::Blue)), bg!(Some(Color::Yellow)), bold!(true),
///     fg!(None), bg!(None), underline!(true),
/// ).unwrap();
/// # }
/// ```
/// Note that the `bold` being set in the first block of control sequences is preserved after the
/// second one.
///
/// And, of course, you can mix ordinary formatting outputs with the control sequences:
///
/// ```
/// # use termcolor_output::colored;
/// # fn write(writer: &mut impl termcolor::WriteColor) {
/// # use termcolor::Color;
/// colored!(writer, "{}{:?}{} unwraps to {}", bold!(true), Some(0), bold!(false), 0).unwrap();
/// # }
/// ```
///
/// [`write!`]: https://doc.rust-lang.org/std/macro.write.html
/// [`std::io::Result<()>`]: https://doc.rust-lang.org/std/io/type.Result.html
#[macro_export]
macro_rules! colored {
    ($($arg:tt)*) => {{
        use $crate::termcolor_output_impl::ColoredOutput;
        use $crate::termcolor::WriteColor;
        use $crate::std::io::Write;
        use $crate::WriteColorGuard;
        #[derive(ColoredOutput)]
        enum __Writer {
            Data = (stringify!($($arg)*), 0).1
        }
        colored_impl!()
    }}
}

/// A convenience function, serving the role of `writeln!` macro.
///
/// This function accepts a closure containing all necessary [`colored`]! calls.
/// It will clear the writer style, run the closure, clear the writer style again
/// and write a newline.
#[allow(unused_imports)]
pub fn colored_ln<W: termcolor::WriteColor, F: FnOnce(&mut W) -> std::io::Result<()>>(
    buf: &mut W,
    func: F,
) -> std::io::Result<()> {
    colored!(buf, "{}", reset!())?;
    func(buf)?;
    colored!(buf, "{}\n", reset!())
}