diff --git a/crates/anstyle-stream/src/auto.rs b/crates/anstyle-stream/src/auto.rs index e9c7190e..08b88112 100644 --- a/crates/anstyle-stream/src/auto.rs +++ b/crates/anstyle-stream/src/auto.rs @@ -28,6 +28,28 @@ where #[cfg(feature = "auto")] #[inline] pub fn new(raw: S, choice: ColorChoice) -> Self { + match choice { + ColorChoice::Auto => Self::auto(raw), + ColorChoice::AlwaysAnsi => Self::always_ansi(raw), + ColorChoice::Always => Self::always(raw), + ColorChoice::Never => Self::never(raw), + } + } + + /// Auto-adapt for the stream's capabilities + #[cfg(feature = "auto")] + #[inline] + pub fn auto(raw: S) -> Self { + let choice = Self::choice(&raw); + debug_assert_ne!(choice, ColorChoice::Auto); + Self::new(raw, choice) + } + + /// Report the desired choice for the given stream + #[cfg(feature = "auto")] + #[inline] + pub fn choice(raw: &S) -> ColorChoice { + let choice = concolor_override::get(); match choice { ColorChoice::Auto => { let clicolor = concolor_query::clicolor(); @@ -41,24 +63,15 @@ where || concolor_query::is_ci()) || concolor_query::clicolor_force() { - Self::always(raw) + ColorChoice::Always } else { - Self::never(raw) + ColorChoice::Never } } - ColorChoice::AlwaysAnsi => Self::always_ansi(raw), - ColorChoice::Always => Self::always(raw), - ColorChoice::Never => Self::never(raw), + ColorChoice::AlwaysAnsi | ColorChoice::Always | ColorChoice::Never => choice, } } - /// Auto-adapt for the stream's capabilities - #[cfg(feature = "auto")] - #[inline] - pub fn auto(raw: S) -> Self { - Self::new(raw, concolor_override::get()) - } - /// Force ANSI escape codes to be passed through as-is, no matter what the inner `Write` /// supports. #[inline] diff --git a/crates/anstyle-stream/src/macros.rs b/crates/anstyle-stream/src/macros.rs index 4597f8e5..99913e70 100644 --- a/crates/anstyle-stream/src/macros.rs +++ b/crates/anstyle-stream/src/macros.rs @@ -214,3 +214,100 @@ macro_rules! eprintln { } }}; } + +/// Panics the current thread. +/// +/// This allows a program to terminate immediately and provide feedback +/// to the caller of the program. +/// +/// This macro is the perfect way to assert conditions in example code and in +/// tests. `panic!` is closely tied with the `unwrap` method of both +/// [`Option`][ounwrap] and [`Result`][runwrap] enums. Both implementations call +/// `panic!` when they are set to [`None`] or [`Err`] variants. +/// +/// When using `panic!()` you can specify a string payload, that is built using +/// the [`format!`] syntax. That payload is used when injecting the panic into +/// the calling Rust thread, causing the thread to panic entirely. +/// +/// The behavior of the default `std` hook, i.e. the code that runs directly +/// after the panic is invoked, is to print the message payload to +/// `stderr` along with the file/line/column information of the `panic!()` +/// call. You can override the panic hook using [`std::panic::set_hook()`]. +/// Inside the hook a panic can be accessed as a `&dyn Any + Send`, +/// which contains either a `&str` or `String` for regular `panic!()` invocations. +/// To panic with a value of another other type, [`panic_any`] can be used. +/// +/// See also the macro [`compile_error!`], for raising errors during compilation. +/// +/// # When to use `panic!` vs `Result` +/// +/// The Rust language provides two complementary systems for constructing / +/// representing, reporting, propagating, reacting to, and discarding errors. These +/// responsibilities are collectively known as "error handling." `panic!` and +/// `Result` are similar in that they are each the primary interface of their +/// respective error handling systems; however, the meaning these interfaces attach +/// to their errors and the responsibilities they fulfill within their respective +/// error handling systems differ. +/// +/// The `panic!` macro is used to construct errors that represent a bug that has +/// been detected in your program. With `panic!` you provide a message that +/// describes the bug and the language then constructs an error with that message, +/// reports it, and propagates it for you. +/// +/// `Result` on the other hand is used to wrap other types that represent either +/// the successful result of some computation, `Ok(T)`, or error types that +/// represent an anticipated runtime failure mode of that computation, `Err(E)`. +/// `Result` is used alongside user defined types which represent the various +/// anticipated runtime failure modes that the associated computation could +/// encounter. `Result` must be propagated manually, often with the the help of the +/// `?` operator and `Try` trait, and they must be reported manually, often with +/// the help of the `Error` trait. +/// +/// For more detailed information about error handling check out the [book] or the +/// [`std::result`] module docs. +/// +/// [ounwrap]: Option::unwrap +/// [runwrap]: Result::unwrap +/// [`std::panic::set_hook()`]: ../std/panic/fn.set_hook.html +/// [`panic_any`]: ../std/panic/fn.panic_any.html +/// [`Box`]: ../std/boxed/struct.Box.html +/// [`Any`]: crate::any::Any +/// [`format!`]: ../std/macro.format.html +/// [book]: ../book/ch09-00-error-handling.html +/// [`std::result`]: ../std/result/index.html +/// +/// # Current implementation +/// +/// If the main thread panics it will terminate all your threads and end your +/// program with code `101`. +/// +/// # Examples +/// +/// ```should_panic +/// # #![allow(unreachable_code)] +/// use anstyle_stream::panic; +/// panic!(); +/// panic!("this is a terrible mistake!"); +/// panic!("this is a {} {message}", "fancy", message = "message"); +/// ``` +#[cfg(feature = "auto")] +#[macro_export] +macro_rules! panic { + () => { + ::std::panic!() + }; + ($($arg:tt)*) => {{ + use std::io::Write as _; + + let panic_stream = std::io::stderr(); + let choice = $crate::AutoStream::choice(&panic_stream); + let buffer = $crate::Buffer::new(); + let mut stream = $crate::AutoStream::new(buffer, choice); + // Ignore errors rather than panic + let _ = ::std::write!(&mut stream, $($arg)*); + let buffer = stream.into_inner(); + // Should be UTF-8 but not wanting to panic + let buffer = String::from_utf8_lossy(buffer.as_bytes()).into_owned(); + ::std::panic!("{}", buffer) + }}; +}