diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d7cde9..b11011d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.1.0-alpha.1] - 2020-11-29 + +- Revamp of logs: only local/gated macros are generated now +- Remove optional semihosting dependency, relegate example implementation to QEMU test +- Relegate stdout/stderr flushers to example submodule +- Simplification of `hex` submodule, removing typenum and implementing all block sizes +- Addition of "truncated" hex formats like `hex_fmt`, remove it as dependency instead + ## [0.1.0-alpha.1] - 2020-11-28 Test the release process, meditate on the `local_*!` issue. diff --git a/Cargo.toml b/Cargo.toml index 5151943..5ea70e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "delog" -version = "0.1.0-alpha.1" +version = "0.1.0-alpha.2" description = "Deferred logging, an implementation and extension of Rust's standard logging facade." authors = ["Nicolas Stalder "] license = "Apache-2.0 OR MIT" @@ -16,18 +16,11 @@ features = ["example"] targets = [] [dependencies] -cortex-m-semihosting = { version = "0.3.5", optional = true } -hex_fmt = "0.3.0" log = "0.4.11" -typenum = "1.12.0" [dev-dependencies] insta = "1.3.0" [features] -default = ["fallible", "immediate"] -fallible = [] -immediate = [] -example = ["std-example-flushers"] -semihosting-example-flushers = ["cortex-m-semihosting"] -std-example-flushers = [] +example = ["std"] +std = [] diff --git a/delog-examples/Cargo.toml b/delog-examples/Cargo.toml index 7f97111..a7b8b65 100644 --- a/delog-examples/Cargo.toml +++ b/delog-examples/Cargo.toml @@ -8,9 +8,11 @@ edition = "2018" [dependencies.delog] path = ".." -features = ["std-example-flushers"] +features = ["example"] [features] default = ["log-all"] log-none = [] log-all = [] + +log-warn = [] diff --git a/delog-examples/examples/around.rs b/delog-examples/examples/around.rs index 15b11c7..281177e 100644 --- a/delog-examples/examples/around.rs +++ b/delog-examples/examples/around.rs @@ -1,9 +1,9 @@ #[macro_use] extern crate delog; -use delog::upstream::info; +use delog::log::info; -use delog::flushers::StdoutFlusher; +use delog::example::StdoutFlusher; delog!(Delogger, 25, StdoutFlusher); diff --git a/delog-examples/examples/log.rs b/delog-examples/examples/log.rs index bb5702e..84874d4 100644 --- a/delog-examples/examples/log.rs +++ b/delog-examples/examples/log.rs @@ -1,10 +1,10 @@ #[macro_use] extern crate delog; -use delog::flushers::StdoutFlusher; +use delog::example::StdoutFlusher; delog!(Delogger, 256, StdoutFlusher); -local_macros!(); +generate_macros!(); static FLUSHER: StdoutFlusher = StdoutFlusher {}; diff --git a/delog-examples/examples/try-log.rs b/delog-examples/examples/try-log.rs index aa13b8d..fd7b0d3 100644 --- a/delog-examples/examples/try-log.rs +++ b/delog-examples/examples/try-log.rs @@ -1,10 +1,13 @@ #[macro_use] extern crate delog; -use delog::flushers::StdoutFlusher; -use delog::renderers::RipgrepRenderer; +use delog::{ + example::StdoutFlusher, + render::RipgrepRenderer, +}; + delog!(Delogger, 196, StdoutFlusher, renderer: RipgrepRenderer); -local_macros!(); +generate_macros!(); static STDOUT_FLUSHER: StdoutFlusher = StdoutFlusher {}; static RENDERER: RipgrepRenderer = RipgrepRenderer {}; @@ -12,22 +15,23 @@ static RENDERER: RipgrepRenderer = RipgrepRenderer {}; fn main() { Delogger::init(delog::LevelFilter::Info, &STDOUT_FLUSHER, &RENDERER).ok(); - // do some serious work - global_try_warn!("This is a warning").unwrap(); - global_try_info!("This is information").unwrap(); - global_try_warn!("This is a warning").unwrap(); - global_try_info!("This is information").expect_err("should error out due to incapacity"); + // // do some serious work + // global_try_warn!("This is a warning").unwrap(); + // global_try_info!("This is information").unwrap(); + // global_try_warn!("This is a warning").unwrap(); + // global_try_info!("This is information").expect_err("should error out due to incapacity"); - // flush the logs - Delogger::flush(); + // // flush the logs + // Delogger::flush(); - println!("==="); + // println!("==="); try_warn!("This is a warning").unwrap(); + try_info!(target: "!", "This is information NOW").unwrap(); try_info!("This is information").unwrap(); - try_warn!(target: "!", "This is a warning").unwrap(); + try_warn_now!("This is a warning").unwrap(); try_warn!("This is a warning").unwrap(); - #[cfg(not(feature = "log-none"))] + #[cfg(not(any(feature = "log-none", feature = "log-warn")))] try_info!("This is information").expect_err("should error out due to incapacity"); #[cfg(feature = "log-none")] try_info!("This is information").ok(); diff --git a/examples/hex.rs b/examples/hex.rs index 7ddcc38..ac8a624 100644 --- a/examples/hex.rs +++ b/examples/hex.rs @@ -1,10 +1,26 @@ -use delog::hex_str; +use delog::{hex_str, hexstr}; fn main() { let buf = [1u8, 2, 3, 0xA1, 0xB7, 0xFF, 0x3]; + println!("'{}'", hexstr!(&buf)); println!("'{}'", hex_str!(&buf)); + println!("'{:4}'", hex_str!(&buf)); + println!("'{:<4}'", hex_str!(&buf)); + println!("'{:>4}'", hex_str!(&buf)); println!("'{}'", hex_str!(&buf, 2)); println!("'{:02x}'", hex_str!(&buf, 2)); println!("'{}'", hex_str!(&buf, 4)); + println!("'{:4}'", hex_str!(&buf, 2)); + println!("'{:<4}'", hex_str!(&buf, 2)); + println!("'{:>4}'", hex_str!(&buf, 2)); println!("'{}'", hex_str!(&buf[..], 4)); + println!("'{}'", hex_str!(&buf, 3)); + println!("'{}'", hex_str!(&buf, 2, sep: "|")); + println!("'{:2}'", hex_str!(&buf, 2, sep: "|")); + println!("'{:3x}'", hex_str!(&buf, 2, sep: "|")); + println!("'{:>3x}'", hex_str!(&buf, 2, sep: "|")); + println!("'{:<3x}'", hex_str!(&buf, 2, sep: "|")); + println!("'{:4}'", hex_str!(&buf, 2, sep: "|")); + println!("'{:5x}'", hex_str!(&buf, 2, sep: "|")); + println!("'{:6}'", hex_str!(&buf, 2, sep: "|")); } diff --git a/gate-tests/Cargo.toml b/gate-tests/Cargo.toml index 71f7ab9..03bbffc 100644 --- a/gate-tests/Cargo.toml +++ b/gate-tests/Cargo.toml @@ -12,12 +12,15 @@ cfg-if = "1" [dependencies.lib-a] path = "lib-a" +[dependencies.lib-a1] +path = "lib-a/lib-a1" + [dependencies.lib-b] path = "lib-b" [dependencies.delog] path = ".." -features = ["std-example-flushers"] +features = ["example"] [features] default = ["verbose-renderer"] diff --git a/gate-tests/Makefile b/gate-tests/Makefile index fdfcee7..63bbfad 100644 --- a/gate-tests/Makefile +++ b/gate-tests/Makefile @@ -2,6 +2,6 @@ something: cargo run cargo run --no-default-features --features lib-a/log-all,lib-b/log-all cargo run --features lib-a/log-all,lib-b/log-all - cargo run --features lib-a/log-all + cargo run --features lib-a/log-all,lib-a1/log-all cargo run --features lib-b/log-all cargo run --features lib-a/log-trace,lib-b/log-error diff --git a/gate-tests/lib-a/Cargo.toml b/gate-tests/lib-a/Cargo.toml index ee40fde..36834b2 100644 --- a/gate-tests/lib-a/Cargo.toml +++ b/gate-tests/lib-a/Cargo.toml @@ -4,6 +4,12 @@ version = "0.1.0" authors = ["Nicolas Stalder "] edition = "2018" +[dependencies] +log = "0.4" + +[dependencies.lib-a1] +path = "lib-a1" + [dependencies.delog] path = "../.." diff --git a/gate-tests/lib-a/lib-a1/Cargo.toml b/gate-tests/lib-a/lib-a1/Cargo.toml index 6be2b15..f2b7f5d 100644 --- a/gate-tests/lib-a/lib-a1/Cargo.toml +++ b/gate-tests/lib-a/lib-a1/Cargo.toml @@ -4,6 +4,18 @@ version = "0.1.0" authors = ["Nicolas Stalder "] edition = "2018" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] +log = "0.4" + +[dependencies.delog] +path = "../../.." + +[features] +log-error = [] +log-warn = ["log-error"] +log-info = ["log-warn"] +log-debug = ["log-info"] +log-trace = ["log-debug"] +log-all = ["log-trace"] +log-none = [] + diff --git a/gate-tests/lib-a/lib-a1/src/lib.rs b/gate-tests/lib-a/lib-a1/src/lib.rs index 31e1bb2..0303fdb 100644 --- a/gate-tests/lib-a/lib-a1/src/lib.rs +++ b/gate-tests/lib-a/lib-a1/src/lib.rs @@ -1,7 +1,21 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - assert_eq!(2 + 2, 4); +delog::generate_macros!(); + +pub fn f() { + submodule::sub_f() +} + +pub mod submodule { + pub fn sub_f() { + log::info!("global info log from lib_a1"); + log::warn!("global info log from lib_a1"); + + // log!(delog::Level::Info, "log level info from lib_a::f"); + // log!(target: "!", delog::Level::Info, "log level info from lib_a::f"); + // log!(target: "!", delog::Level::Warn, "log level warn from lib_a::f"); + + info!("local info from lib_a1::f"); + warn!("local warn from lib_a1::f"); + info_now!("immediate local info from lib_a1::f"); + warn_now!("immediate local warn from lib_a1::f"); } } diff --git a/gate-tests/lib-a/src/lib.rs b/gate-tests/lib-a/src/lib.rs index ad7aba7..9f05e1f 100644 --- a/gate-tests/lib-a/src/lib.rs +++ b/gate-tests/lib-a/src/lib.rs @@ -1,15 +1,22 @@ -delog::local_macros!(); +delog::generate_macros!(); pub fn f() { - info!("global info log from lib_a"); - warn!("global info log from lib_a"); + submodule::sub_f(); + lib_a1::f(); +} - log!(delog::Level::Info, "log level info from lib_a::f"); - log!(target: "!", delog::Level::Info, "log level info from lib_a::f"); - log!(target: "!", delog::Level::Warn, "log level warn from lib_a::f"); +pub mod submodule { + pub fn sub_f() { + log::info!("global info log from lib_a"); + log::warn!("global info log from lib_a"); - info!("local info from lib_a::f"); - warn!("local warn from lib_a::f"); - info!(target: "!", "immediate local info from lib_a::f"); - warn!(target: "!", "immediate local warn from lib_a::f"); + // log!(delog::Level::Info, "log level info from lib_a::f"); + // log!(target: "!", delog::Level::Info, "log level info from lib_a::f"); + // log!(target: "!", delog::Level::Warn, "log level warn from lib_a::f"); + + info!("local info from lib_a::f"); + warn!("local warn from lib_a::f"); + info_now!("immediate local info from lib_a::f"); + warn_now!("immediate local warn from lib_a::f"); + } } diff --git a/gate-tests/lib-b/src/lib.rs b/gate-tests/lib-b/src/lib.rs index 702ea11..3698014 100644 --- a/gate-tests/lib-b/src/lib.rs +++ b/gate-tests/lib-b/src/lib.rs @@ -1,12 +1,14 @@ -delog::local_macros!(); +delog::generate_macros!(); + +use delog::log; pub fn g() { - delog::upstream::info!("global info from B"); + log::info!("global info from B"); warn!("info from B"); log!(delog::Level::Info, "log level info from B"); - log!(target: "!", delog::Level::Info, "info from B"); - log!(target: "!", delog::Level::Warn, "warn from B"); + log!(target: "!", delog::Level::Info, "log! ! info from B"); + log!(target: "!", delog::Level::Warn, "log! ! warn from B"); info!("info from B"); warn!("warn from B"); diff --git a/gate-tests/src/main.rs b/gate-tests/src/main.rs index 91283b2..f684cd5 100644 --- a/gate-tests/src/main.rs +++ b/gate-tests/src/main.rs @@ -1,17 +1,17 @@ #[macro_use] extern crate delog; -use delog::flushers::StdoutFlusher; +use delog::example::StdoutFlusher; cfg_if::cfg_if! { if #[cfg(feature = "verbose-renderer")] { - use delog::renderers::RipgrepRenderer; + use delog::render::RipgrepRenderer; delog!(Delogger, 4096, StdoutFlusher, renderer: RipgrepRenderer); static RENDERER: RipgrepRenderer = RipgrepRenderer {}; } else { - use delog::renderers::ArgumentsRenderer; + use delog::render::DefaultRenderer; delog!(Delogger, 4096, StdoutFlusher); - static RENDERER: ArgumentsRenderer = ArgumentsRenderer {}; + static RENDERER: DefaultRenderer = DefaultRenderer {}; } } @@ -21,7 +21,7 @@ fn main() { Delogger::init(delog::LevelFilter::Info, &FLUSHER, &RENDERER).expect("all good"); lib_a::f(); lib_b::g(); - println!("{:?}", delog::trylogger().unwrap().statistics()); + println!("{:?}", delog::logger().unwrap().statistics()); Delogger::flush(); - println!("{:?}", delog::trylogger().unwrap().statistics()); + println!("{:?}", delog::logger().unwrap().statistics()); } diff --git a/qemu-tests/Cargo.toml b/qemu-tests/Cargo.toml index a02ab8b..695affc 100644 --- a/qemu-tests/Cargo.toml +++ b/qemu-tests/Cargo.toml @@ -14,7 +14,6 @@ panic-semihosting = { version = "0.5.3", features = ["exit"] } [dependencies.delog] path = ".." -features = ["semihosting-example-flushers"] [features] default = ["log-all"] diff --git a/qemu-tests/src/main.rs b/qemu-tests/src/main.rs index 1696910..f87c289 100644 --- a/qemu-tests/src/main.rs +++ b/qemu-tests/src/main.rs @@ -17,7 +17,7 @@ impl delog::Flusher for SemihostingFlusher { } } -delog::local_macros!(); +delog::generate_macros!(); delog!(Delogger, 256, SemihostingFlusher); @@ -43,7 +43,7 @@ fn main() -> ! { test_runs(); - debug_now!("{:?}", delog::trylogger().unwrap().statistics()); + debug_now!("{:?}", delog::logger().unwrap().statistics()); info_now!("All tests passed"); debug::exit(debug::EXIT_SUCCESS); diff --git a/src/example.rs b/src/example.rs index b9c55c5..5f35a51 100644 --- a/src/example.rs +++ b/src/example.rs @@ -1,14 +1,21 @@ //! An example deferred logger, generated as -//! `delog!(Delogger, 4096, StdoutFlusher, ArgumentsRenderer)`. +//! `delog!(Delogger, 4096, StderrFlusher, DefaultRenderer)`. //! //! It is included here for documentation purposes only. //! //! Do ensure that the `example` feature is not active in production! //! //! ``` -//! use delog::flushers::StdoutFlusher; -//! delog!(Delogger, 256, StdoutFlusher); -//! static STDOUT_FLUSHER: StdoutFlusher = StdoutFlusher {}; +//! pub struct StderrFlusher {} +//! +//! impl crate::Flusher for StderrFlusher { +//! fn flush(&self, logs: &str) { +//! print!("{}", logs); +//! } +//! } +//! +//! delog!(Delogger, 256, StderrFlusher); +//! static FLUSHER: StderrFlusher = StderrFlusher {}; //! Delogger::init(log::LevelFilter::Info, &STDOUT_FLUSHER).ok(); //! //! warn!("This is a warning"); @@ -18,10 +25,31 @@ //! Delogger::flush(); //! ``` -use crate::flushers::StdoutFlusher; -use crate::renderers::ArgumentsRenderer; +// use crate::flushers::StderrFlusher; +use crate::render::DefaultRenderer; -crate::delog!(Delogger, 4096, StdoutFlusher, renderer: ArgumentsRenderer); +#[derive(Debug, Default)] +/// Flushes logs to stderr. +pub struct StderrFlusher {} -crate::local_macros!(); +impl crate::Flusher for StderrFlusher { + fn flush(&self, logs: &str) { + print!("{}", logs); + } +} + +#[derive(Debug, Default)] +/// Flushes logs to stdout. +pub struct StdoutFlusher {} + +impl crate::Flusher for StdoutFlusher { + fn flush(&self, logs: &str) { + print!("{}", logs); + } +} + +crate::delog!(Delogger, 4096, StderrFlusher, renderer: DefaultRenderer); + +// #[macro_export] +// crate::generate_macros!(); diff --git a/src/flushers.rs b/src/flushers.rs index 2e18737..06a8505 100644 --- a/src/flushers.rs +++ b/src/flushers.rs @@ -6,12 +6,35 @@ //! selected. Additionally setting `std` gives stdout/stderr flushers, //! while additionally setting `semihosting` gives flushers to host's stdout/stderr. -#[cfg(any(feature = "std-example-flushers", test))] -mod std; -#[cfg(any(feature = "std-example-flushers", test))] -pub use crate::flushers::std::*; +// #[cfg(any(feature = "std-example-flushers", test))] +// mod std; +// #[cfg(any(feature = "std-example-flushers", test))] +// pub use crate::flushers::std::*; + +// // #[cfg(feature = "semihosting-example-flushers")] +// // mod semihosting; +// // #[cfg(feature = "semihosting-example-flushers")] +// // pub use crate::flushers::semihosting::*; + +use crate::Flusher; + +#[derive(Debug, Default)] +/// Flushes logs to stdout. +pub struct StdoutFlusher {} + +impl Flusher for StdoutFlusher { + fn flush(&self, logs: &str) { + print!("{}", logs); + } +} + +#[derive(Debug, Default)] +/// Flushes logs to stderr. +pub struct StderrFlusher {} + +impl Flusher for StderrFlusher { + fn flush(&self, logs: &str) { + eprint!("{}", logs); + } +} -#[cfg(feature = "semihosting-example-flushers")] -mod semihosting; -#[cfg(feature = "semihosting-example-flushers")] -pub use crate::flushers::semihosting::*; diff --git a/src/hex.rs b/src/hex.rs index 7e65c6e..62db4c1 100644 --- a/src/hex.rs +++ b/src/hex.rs @@ -39,14 +39,49 @@ use core::marker::PhantomData; use core::fmt; -/// re-export from `hex_fmt` -/// -pub use hex_fmt::HexFmt; -/// re-export from `hex_fmt` -/// -pub use hex_fmt::HexList; +///// re-export from `hex_fmt` +///// +//pub use hex_fmt::HexFmt; +///// re-export from `hex_fmt` +///// +//pub use hex_fmt::HexList; -pub use typenum::{consts, Unsigned}; +/// A type that specifies an unsigned integer. +/// +/// We use this instead of `typenum` as the latter currently lacks +/// a mapping from `usize` to the associated type. +pub trait Unsigned { + /// The actual number. + const N: usize; +} + +/// Sorry little replacement for the missing int to Unsigned type map in `typenum`. +#[macro_export] +#[doc(hidden)] +macro_rules! typeint { + ($name:ident, $n:expr) => { + /// A type that represents the integer `N`. + pub struct $name {} + impl $crate::hex::Unsigned for $name { + const N: usize = $n; + } + } +} + +typeint!(U1, 1); +typeint!(U2, 1); +typeint!(U3, 1); +typeint!(U4, 1); +typeint!(U5, 1); +typeint!(U6, 1); +typeint!(U7, 1); +typeint!(U8, 1); + +// /// A type representing the number 1. +// pub struct U1 {} +// impl Unsigned for U1 { +// const N: usize = 1; +// } /// A type that specifies a separator str. pub trait Separator { @@ -65,12 +100,19 @@ impl Separator for SpaceSeparator { const SEPARATOR: &'static str = " "; } +// /// New approach. +// pub trait HexStrTrait { +// const BYTES_PER_BLOCK: usize; +// const SEPARATOR: &'static str; +// fn bytes(&self) -> &[u8]; +// } + /// Zero-sized wrapper newtype, allowing grouping bytes in blocks of N hexadecimals /// during formatting. /// /// Use the method with the same name to construct this from your byte array or slice, /// or preferrably the `hex_str!` or `hexstr!` macro. -pub struct HexStr<'a, T: ?Sized, S=SpaceSeparator, BytesPerBlock=consts::U1> +pub struct HexStr<'a, T: ?Sized, S=SpaceSeparator, BytesPerBlock=U1> where S: Separator, BytesPerBlock: Unsigned, @@ -81,34 +123,21 @@ where _block_size: PhantomData, } -/// Sorry little replacement for the missing int to Unsigned type map in `typenum`. -// this doesn't really help with the below, as it doesn't match on passed $n:literal values -// hoping for: https://github.com/paholg/typenum/pull/136 -#[macro_export] -#[doc(hidden)] -macro_rules! typeint { - (1) => { $crate::hex::consts::U1 }; - (2) => { $crate::hex::consts::U2 }; - (3) => { $crate::hex::consts::U3 }; - (4) => { $crate::hex::consts::U4 }; - (5) => { $crate::hex::consts::U5 }; - (6) => { $crate::hex::consts::U6 }; - (7) => { $crate::hex::consts::U7 }; - (8) => { $crate::hex::consts::U8 }; - (9) => { $crate::hex::consts::U9 }; - (10) => { $crate::hex::consts::U10 }; - (16) => { $crate::hex::consts::U16 }; - (20) => { $crate::hex::consts::U20 }; - (32) => { $crate::hex::consts::U32 }; - (64) => { $crate::hex::consts::U64 }; -} +// /// Sorry little replacement for the missing int to Unsigned type map in `typenum`. +// #[macro_export] +// #[doc(hidden)] +// macro_rules! typeint { +// ($n:expr) => { +// struct Number {}; +// impl $crate::hex::Unsigned for Number { +// const N: usize = $n; +// } +// } +// } #[macro_export] /// Compactly format byte arrays and slices as hexadecimals. /// -/// Exposes the `hex_str` function as macro, for ease of practical use via the -/// latter's "namespace-piercing" capabilities. -/// /// The second parameter refers to the number of bytes in a block (separated by spaces). /// /// ``` @@ -116,44 +145,26 @@ macro_rules! typeint { /// let four_bytes = &[7u8, 0xA1, 255, 0xC7]; /// assert_eq!(format!("{:x}", hex_str!(four_bytes)), "07 a1 ff c7"); /// assert_eq!(format!("{}", hex_str!(four_bytes, 2)), "07A1 FFC7"); -/// assert_eq!(format!("{}", hex_str!(four_bytes, 2, "|")), "07A1|FFC7"); +/// assert_eq!(format!("{}", hex_str!(four_bytes, 2, sep: "|")), "07A1|FFC7"); /// assert_eq!(format!("{}", hex_str!(four_bytes, 3)), "07A1FF C7"); /// ``` macro_rules! hex_str { - ($array:expr) => { $crate::hex::hex_str($array) }; - ($array:expr, 1) => { $crate::hex::hex_str_1($array) }; - // ($array:expr, 2) => { $crate::hex::hex_str_2($array) }; - ($array:expr, 2) => { $crate::hex_str!($array, 2, " ") }; - ($array:expr, 3) => { $crate::hex::hex_str_3($array) }; - ($array:expr, 4) => { $crate::hex::hex_str_4($array) }; - ($array:expr, 5) => { $crate::hex::hex_str_5($array) }; - ($array:expr, 8) => { $crate::hex::hex_str_8($array) }; - ($array:expr, 16) => { $crate::hex::hex_str_16($array) }; - ($array:expr, 20) => { $crate::hex::hex_str_20($array) }; - ($array:expr, 32) => { $crate::hex::hex_str_32($array) }; - ($array:expr, 64) => { $crate::hex::hex_str_64($array) }; - ($array:expr, 2, $separator:expr) => {{ + ($array:expr) => { $crate::hex_str!($array, 1, sep: " ") }; + ($array:expr, sep: $separator:expr) => { $crate::hex_str!($array, 1, sep: $separator) }; + ($array:expr, $n:tt) => { $crate::hex_str!($array, $n, sep: " ") }; + ($array:expr, $n:tt, sep: $separator:expr) => {{ struct Separator {} impl $crate::hex::Separator for Separator { const SEPARATOR: &'static str = $separator; } - $crate::hex::HexStr::<_, Separator, $crate::typeint!(2)>($array) + $crate::typeint!(Number, $n); + $crate::hex::HexStr::<_, Separator, Number>($array) }}; - // ($array:expr, $n:literal, $separator:expr) => {{ - // struct Separator {} - // impl $crate::hex::Separator for Separator { - // const SEPARATOR: &'static str = $separator; - // } - // $crate::hex::HexStr::<_, Separator, $crate::typeint!($n)>($array) - // }}; } #[macro_export] /// More compactly format byte arrays and slices as hexadecimals. /// -/// Exposes the `hexstr` function (no spaces) as macro, for ease of practical use via the -/// latter's "namespace-piercing" capabilities. -/// /// ``` /// use delog::hexstr; /// let four_bytes = &[7u8, 0xA1, 255, 0xC7]; @@ -161,115 +172,38 @@ macro_rules! hex_str { /// assert_eq!(format!("{:x}", hexstr!(four_bytes)), "07a1ffc7"); /// ``` macro_rules! hexstr { - ($array:expr) => { $crate::hex::hexstr($array) }; + ($array:expr) => { + $crate::hex_str!($array, sep: "") + } } #[allow(non_snake_case)] -/// dive into `typenum` and discover your inner traitist +/// dive into types and discover your inner traitist /// -/// The first parameter denotes the separator, the second `typenum` parameter -/// denotes the block size in bytes, e.g. `consts::U7` means blocks of 7 bytes (or 56 bits). +/// The first parameter denotes the separator, the second `Unsigned` parameter +/// denotes the block size in bytes, e.g. `typeint!(U7, 7)` creates a type `U7` +/// which means blocks of 7 bytes (or 56 bits). /// /// In most cases, using one of the macros will suffice and is preferrable. /// /// ``` -/// use delog::hex::{HexStr, Separator, consts}; +/// use delog::hex::{HexStr, Separator, Unsigned}; /// struct Pipe {} /// impl Separator for Pipe { /// const SEPARATOR: &'static str = "|"; /// } +/// struct U3 {} +/// impl Unsigned for U3 { +/// const N: usize = 3; +/// } /// let four_bytes = &[7u8, 0xA1, 255, 0xC7]; -/// let hex_str = HexStr::<_, Pipe, consts::U3>(four_bytes); +/// let hex_str = HexStr::<_, Pipe, U3>(four_bytes); /// assert_eq!(format!("{}", hex_str), "07A1FF|C7"); /// ``` pub fn HexStr(value: &T) -> HexStr { HexStr { value, _separator: PhantomData, _block_size: PhantomData } } -/// blocks of 1 byte / 8 bits in hex, no space in between (e.g., `4A121387`) -/// -/// For ease of use, prefer the `hexstr!(value)` macro. -pub fn hexstr(value: &T) -> HexStr { - HexStr(value) -} - -/// synonym for `hex_str_1`, like ISO 7816 if enclosed in single quotes (`'8A 4F 12 AA'`). -/// -/// For ease of use, prefer the `hex_str!(value)` macro. -pub fn hex_str(value: &T) -> HexStr { - HexStr(value) -} - -/// blocks of 1 byte / 8 bits in hex, space in between (e.g., `4A 12 13 87`) -/// -/// For ease of use, prefer the `hex_str!(value)` macro. -pub fn hex_str_1(value: &T) -> HexStr { - HexStr(value) -} - -/// blocks of 2 bytes / 16 bits in hex, space in between (e.g., `4A12 1387`) -/// -/// For ease of use, prefer the `hex_str!(value, 2)` macro. -pub fn hex_str_2(value: &T) -> HexStr { - HexStr(value) -} - -/// blocks of 3 bytes / 24 bits in hex, space in between (e.g., `4A1213 871234 ABCD`) -/// -/// For ease of use, prefer the `hex_str!(value, 3)` macro. -pub fn hex_str_3(value: &T) -> HexStr { - HexStr(value) -} - -/// blocks of 4 bytes / 32 bits in hex, space in between (e.g., `4A121387 1234ABCD`) -/// -/// For ease of use, prefer the `hex_str!(value, 4)` macro. -pub fn hex_str_4(value: &T) -> HexStr { - HexStr(value) -} - -/// blocks of 5 bytes / 40 bits in hex, space in between (e.g., `4A12138712 34ABCD`) -/// -/// For ease of use, prefer the `hex_str!(value, 5)` macro. -pub fn hex_str_5(value: &T) -> HexStr { - HexStr(value) -} - -/// blocks of 8 bytes / 64 bits in hex, space in between -/// -/// For ease of use, prefer the `hex_str!(value, 8)` macro. -pub fn hex_str_8(value: &T) -> HexStr { - HexStr(value) -} - -/// blocks of 16 bytes / 128 bits in hex, space in between -/// -/// For ease of use, prefer the `hex_str!(value, 16)` macro. -pub fn hex_str_16(value: &T) -> HexStr { - HexStr(value) -} - -/// blocks of 20 bytes / 160 bits in hex, space in between -/// -/// For ease of use, prefer the `hex_str!(value, 20)` macro. -pub fn hex_str_20(value: &T) -> HexStr { - HexStr(value) -} - -/// blocks of 32 bytes / 256 bits in hex, space in between -/// -/// For ease of use, prefer the `hex_str!(value, 32)` macro. -pub fn hex_str_32(value: &T) -> HexStr { - HexStr(value) -} - -/// blocks of 64 bytes / 512 bits in hex, space in between -/// -/// For ease of use, prefer the `hex_str!(value, 64)` macro. -pub fn hex_str_64(value: &T) -> HexStr { - HexStr(value) -} - impl fmt::Debug for HexStr<'_, T, S, U> where T: AsRef<[u8]>, @@ -303,19 +237,55 @@ macro_rules! implement { U: Unsigned, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - let mut first = true; - for entry in self.value.as_ref().chunks(U::USIZE) { - if !first { - f.write_str(S::SEPARATOR)?; - } else { - first = false; - } - for byte in entry.iter() { - write!(f, $padded_formatter, byte)?; - // fmt::$Trait::fmt(byte, f)?; + use core::fmt::{self, Alignment::*}; + let max_bytes = f.width().unwrap_or(usize::MAX); + let bytes = self.value.as_ref(); + + #[inline] + fn nontruncated_fmt( + bytes: &[u8], f: &mut fmt::Formatter<'_>, + chunk_size: usize, separator: &str, + ) -> Result<(), fmt::Error> { + let mut first = true; + for entry in bytes.chunks(chunk_size) { + if first { + first = false; + } else { + f.write_str(separator)?; + } + for byte in entry.iter() { + write!(f, $padded_formatter, byte)?; + } } + Ok(()) + } + + // const ELLIPSIS: &str = "…"; + const ELLIPSIS: &str = ".."; + + let chunk_size = U::N; + let separator = S::SEPARATOR; + + if bytes.len() <= max_bytes { + nontruncated_fmt(bytes, f, chunk_size, separator) + } else { + let align = f.align().unwrap_or(Center); + let (left, right) = match align { + Left => (max_bytes, 0), + Center => (max_bytes - max_bytes/2, max_bytes/2), + Right => (0, max_bytes), + }; + nontruncated_fmt(&bytes[..left], f, chunk_size, separator)?; + // if left > 0 { + // f.write_str(separator)?; + // } + f.write_str(ELLIPSIS)?; + // if right > 0 { + // f.write_str(separator)?; + // } + nontruncated_fmt(&bytes[bytes.len()-right..], f, chunk_size, separator)?; + Ok(()) } - Ok(()) } } } @@ -331,21 +301,22 @@ mod test { #[test] fn test_hex_str() { let buf = [1u8, 2, 3, 0xA1, 0xB7, 0xFF, 0x3]; - insta::assert_debug_snapshot!(format_args!("'{:02X}'", hex_str_1(&buf))); - insta::assert_debug_snapshot!(format_args!("'{:02X}'", hex_str_2(&buf))); - insta::assert_debug_snapshot!(format_args!("'{:02x}'", hex_str_2(&buf))); - insta::assert_debug_snapshot!(format_args!("'{:02X}'", hex_str_4(&buf))); - insta::assert_debug_snapshot!(format_args!("'{:02X}'", hex_str_4(&buf[..]))); - insta::assert_debug_snapshot!(format_args!("'{:02X}'", hex_str_4(&buf))); - insta::assert_debug_snapshot!(format_args!("'{:X}'", hex_str_4(&buf))); + insta::assert_debug_snapshot!(format_args!("'{}'", hex_str!(&buf))); + insta::assert_debug_snapshot!(format_args!("'{}'", hex_str!(&buf, 2))); + insta::assert_debug_snapshot!(format_args!("'{:x}'", hex_str!(&buf, 2))); + insta::assert_debug_snapshot!(format_args!("'{}'", hex_str!(&buf, 4))); + insta::assert_debug_snapshot!(format_args!("'{}'", hex_str!(&buf[..], 4))); + insta::assert_debug_snapshot!(format_args!("'{}'", hex_str!(&buf, 4))); + insta::assert_debug_snapshot!(format_args!("'{}'", hex_str!(&buf, 4))); } #[test] fn test_custom_hex_str() { let buf = [1u8, 2, 3, 0xA1, 0xB7, 0xFF, 0x3]; + typeint!(U3, 3); insta::assert_debug_snapshot!(format_args!( - "'{:02X}'", - HexStr::<_, SpaceSeparator, consts::U3>(&buf), + "'{:X}'", + HexStr::<_, SpaceSeparator, U3>(&buf), )); } diff --git a/src/lib.rs b/src/lib.rs index 93157d1..eb84cea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -65,29 +65,30 @@ //! #![deny(missing_docs)] -#![cfg_attr(not(any(feature = "std-example-flushers", test)), no_std)] +#![cfg_attr(not(any(feature = "std", test)), no_std)] use core::fmt; -pub use log as upstream; -pub use log::{Level, LevelFilter, Record}; +pub use log; // as upstream; +pub use log::{Level, LevelFilter, Record, log_enabled}; // TODO: figure out how to re-export `log` as module and `log!` as macro // This way, at least we can re-export `log!`, but in a weird twist of fate, // it also gets re-exported as `upstream!` (huh?!) // pub use log::{debug, error, info, log, log_enabled, trace, warn}; -pub use log::log_enabled; #[cfg(feature = "example")] pub mod example; -#[cfg(any(feature = "std-example-flushers", feature = "semihosting-example-flushers"))] -pub mod flushers; +// // #[cfg(any(feature = "std-example-flushers", feature = "semihosting-example-flushers"))] +// #[cfg(feature = "std")] +// pub mod flushers; -pub mod renderers; +// pub mod renderers; pub mod hex; -mod log_macros_global; -mod log_macros_local; +// mod log_macros_global; +// mod log_macros_local; +mod macros; mod logger; pub use logger::{Delogger, Statistics, TryLog, TryLogWithStatistics, dequeue, enqueue, try_enqueue}; @@ -114,7 +115,7 @@ pub trait Renderer: Send + Sync { static mut LOGGER: Option<&'static dyn logger::TryLogWithStatistics> = None; /// Returns a reference to the logger (as `TryLogWithStatistics` implementation) -pub fn trylogger() -> &'static mut Option<&'static dyn logger::TryLogWithStatistics> { +pub fn logger() -> &'static mut Option<&'static dyn logger::TryLogWithStatistics> { unsafe { &mut LOGGER } } @@ -125,7 +126,7 @@ pub fn __private_api_try_log( level: log::Level, &(target, module_path, file, line): &(&str, &'static str, &'static str, u32), ) -> core::result::Result<(), ()> { - trylogger().ok_or(())?.try_log( + crate::logger().ok_or(())?.try_log( &log::Record::builder() .args(args) .level(level) @@ -144,7 +145,7 @@ pub fn __private_api_try_log_lit( level: log::Level, &(target, module_path, file, line): &(&str, &'static str, &'static str, u32), ) -> core::result::Result<(), ()> { - trylogger().ok_or(())?.try_log( + crate::logger().ok_or(())?.try_log( &log::Record::builder() .args(format_args!("{}", message)) .level(level) diff --git a/src/log_macros_local.rs b/src/log_macros_local.rs deleted file mode 100644 index 39afbe7..0000000 --- a/src/log_macros_local.rs +++ /dev/null @@ -1,316 +0,0 @@ -// There is a syntax issue with "repetitions in binding patterns for nested macros", -// with a workaround: https://github.com/rust-lang/rust/issues/35853#issuecomment-443110660 -// -// This is why we want to have `($)` expressions in the following, can just imagine they're not there. -// -// Unfortunately, I couldn't get this to work, so instead we use the weird `with_dollar_sign!` instead. - -#[macro_export] -#[doc(hidden)] -macro_rules! with_dollar_sign { - ($($body:tt)*) => { - macro_rules! __with_dollar_sign { $($body)* } - __with_dollar_sign!($); - } -} - -/// Generate logging macros that can be gated by library. -/// -/// Realize that these macros are generated **in the namespace of the consuming library**, the one -/// that actally later makes calls to `local_warn!` etc. -/// -/// To see this in action, compile documentation using `cargo doc --features example`, or inspect -/// the `gate-tests/` subdirectory. -#[macro_export] -macro_rules! local_macros { - () => { - $crate::with_dollar_sign! { - ($d:tt) => { - - /// Local version of `log!`. - #[macro_export(local_inner_macros)] - macro_rules! log { - (target: $target:expr, $lvl:expr, $message:expr) => ( - #[cfg(all(any(feature = "log-all", feature = "log-info"), not(feature = "log-none")))] - $crate::upstream::log!(target: $target, $lvl, $message)); - (target: $target:expr, $lvl:expr, $d($arg:tt)+) => ( - #[cfg(all(any(feature = "log-all", feature = "log-info"), not(feature = "log-none")))] - $crate::upstream::log!(target: $target, $lvl, $d($arg)+)); - ($lvl:expr, $d($arg:tt)+) => ( - #[cfg(all(any(feature = "log-all", feature = "log-info"), not(feature = "log-none")))] - $crate::upstream::log!($lvl, $d($arg)+)); - } - - /// Local version of `debug!`. - #[macro_export(local_inner_macros)] - macro_rules! debug { - (target: $target:expr, $d($arg:tt)+) => ( - #[cfg(all(any(feature = "log-all", feature = "log-debug"), not(feature = "log-none")))] - $crate::upstream::debug!(target: $target, $d($arg)+)); - ($d($arg:tt)+) => ( - #[cfg(all(any(feature = "log-all", feature = "log-debug"), not(feature = "log-none")))] - $crate::upstream::debug!($d($arg)+)); - } - - /// Local version of `error!`. - #[macro_export(local_inner_macros)] - macro_rules! error { - (target: $target:expr, $d($arg:tt)+) => ( - #[cfg(all(any(feature = "log-all", feature = "log-error"), not(feature = "log-none")))] - $crate::upstream::error!(target: $target, $d($arg)+)); - ($d($arg:tt)+) => ( - #[cfg(all(any(feature = "log-all", feature = "log-error"), not(feature = "log-none")))] - $crate::upstream::error!($d($arg)+)); - } - - /// Local version of `info!`. - #[macro_export(local_inner_macros)] - macro_rules! info { - (target: $target:expr, $d($arg:tt)+) => ( - #[cfg(all(any(feature = "log-all", feature = "log-info"), not(feature = "log-none")))] - $crate::upstream::info!(target: $target, $d($arg)+)); - ($d($arg:tt)+) => ( - #[cfg(all(any(feature = "log-all", feature = "log-info"), not(feature = "log-none")))] - $crate::upstream::info!($d($arg)+)); - } - - /// Local version of `trace!`. - #[macro_export(local_inner_macros)] - macro_rules! trace { - (target: $target:expr, $d($arg:tt)+) => ( - #[cfg(all(any(feature = "log-all", feature = "log-trace"), not(feature = "log-none")))] - $crate::upstream::trace!(target: $target, $d($arg)+)); - ($d($arg:tt)+) => ( - #[cfg(all(any(feature = "log-all", feature = "log-trace"), not(feature = "log-none")))] - $crate::upstream::trace!($d($arg)+)); - } - - /// Local version of `warn!`. - #[macro_export(local_inner_macros)] - macro_rules! warn { - (target: $target:expr, $d($arg:tt)+) => ( - #[cfg(all(any(feature = "log-all", feature = "log-warn"), not(feature = "log-none")))] - $crate::upstream::warn!(target: $target, $d($arg)+)); - ($d($arg:tt)+) => ( - #[cfg(all(any(feature = "log-all", feature = "log-warn"), not(feature = "log-none")))] - $crate::upstream::warn!($d($arg)+)); - } - - /// Immediate version of `log!`. - macro_rules! log_now { - ($lvl:expr, $d($arg:tt)+) => ( - log!(target: "!", $lvl, $d($arg)+) - ); - } - - /// Immediate version of `debug!`. - macro_rules! debug_now { - ($d($arg:tt)+) => ( - debug!(target: "!", $d($arg)+) - ); - } - - /// Immediate version of `error!`. - macro_rules! error_now { - ($d($arg:tt)+) => ( - error!(target: "!", $d($arg)+) - ); - } - - /// Immediate version of `info!`. - macro_rules! info_now { - ($d($arg:tt)+) => ( - info!(target: "!", $d($arg)+) - ); - } - - /// Immediate version of `trace!`. - macro_rules! trace_now { - ($d($arg:tt)+) => ( - trace!(target: "!", $d($arg)+) - ); - } - - /// Immediate version of `warn!`. - macro_rules! warn_now { - ($d($arg:tt)+) => ( - warn!(target: "!", $d($arg)+) - ); - } - - // /// Fallible version of `info!`. - // #[macro_export(local_inner_macros)] - // macro_rules! try_info { - // (target: $target:expr, $($arg:tt)+) => ( - // try_log!(target: $target, $crate::Level::Info, $($arg)+) - // ); - // ($($arg:tt)+) => ( - // try_log!($crate::Level::Info, $($arg)+) - // ) - // } - - // /// Fallible version of `log!`. - // #[macro_export(local_inner_macros)] - // macro_rules! try_log { - // (target: $target:expr, $lvl:expr, $message:expr) => ( - // #[cfg(all(any(feature = "log-all", feature = "log-info"), not(feature = "log-none")))] - // $crate::global_try_log!(target: $target, $lvl, $message)); - // (target: $target:expr, $lvl:expr, $d($arg:tt)+) => ( - // #[cfg(all(any(feature = "log-all", feature = "log-info"), not(feature = "log-none")))] - // $crate::upstream::log!(target: $target, $lvl, $d($arg)+)); - // ($lvl:expr, $d($arg:tt)+) => ( - // #[cfg(all(any(feature = "log-all", feature = "log-info"), not(feature = "log-none")))] - // $crate::upstream::log!($lvl, $d($arg)+)); - // } - - // /// Fallible version of `info!`. - // #[macro_export(local_inner_macros)] - // macro_rules! try_info { - - // (target: $target:expr, $d($arg:tt)+) => ( - // #[cfg(all(any(feature = "log-all", feature = "log-info"), not(feature = "log-none")))] - // { - // $crate::global_try_info!(target: $target, $d($arg)+) - // } - // #[cfg(not(all(any(feature = "log-all", feature = "log-info"), not(feature = "log-none"))))] - // { - // Ok(()) - // } - // ); - - // ($d($arg:tt)+) => ( - // #[cfg(all(any(feature = "log-all", feature = "log-info"), not(feature = "log-none")))] - // { - // $crate::global_try_info!($d($arg)+) - // } - // #[cfg(not(all(any(feature = "log-all", feature = "log-info"), not(feature = "log-none"))))] - // { - // Ok(()) - // } - // ); - // } - - /// Fallible version of `debug!`. - #[cfg(all(any(feature = "log-all", feature = "log-debug"), not(feature = "log-none")))] - #[macro_export(local_inner_macros)] - macro_rules! try_debug { - - (target: $target:expr, $d($arg:tt)+) => ( - $crate::global_try_debug!(target: $target, $d($arg)+) - ); - - ($d($arg:tt)+) => ( - $crate::global_try_debug!($d($arg)+) - ); - } - - /// Fallible version of `debug!`. - #[cfg(not(all(any(feature = "log-all", feature = "log-debug"), not(feature = "log-none"))))] - #[macro_export(local_inner_macros)] - macro_rules! try_debug { - - // (target: $target:expr, $d($arg:tt)+) => ( Ok() ); - - ($d($arg:tt)+) => ( core::result::Result::<(), ()>::Ok(()) ); - } - - /// Fallible version of `error!`. - #[cfg(all(any(feature = "log-all", feature = "log-error"), not(feature = "log-none")))] - #[macro_export(local_inner_macros)] - macro_rules! try_error { - - (target: $target:expr, $d($arg:tt)+) => ( - $crate::global_try_error!(target: $target, $d($arg)+) - ); - - ($d($arg:tt)+) => ( - $crate::global_try_error!($d($arg)+) - ); - } - - /// Fallible version of `error!`. - #[cfg(not(all(any(feature = "log-all", feature = "log-error"), not(feature = "log-none"))))] - #[macro_export(local_inner_macros)] - macro_rules! try_error { - - // (target: $target:expr, $d($arg:tt)+) => ( Ok() ); - - ($d($arg:tt)+) => ( core::result::Result::<(), ()>::Ok(()) ); - } - - /// Fallible version of `info!`. - #[cfg(all(any(feature = "log-all", feature = "log-info"), not(feature = "log-none")))] - #[macro_export(local_inner_macros)] - macro_rules! try_info { - - (target: $target:expr, $d($arg:tt)+) => ( - $crate::global_try_info!(target: $target, $d($arg)+) - ); - - ($d($arg:tt)+) => ( - $crate::global_try_info!($d($arg)+) - ); - } - - /// Fallible version of `info!`. - #[cfg(not(all(any(feature = "log-all", feature = "log-info"), not(feature = "log-none"))))] - #[macro_export(local_inner_macros)] - macro_rules! try_info { - - // (target: $target:expr, $d($arg:tt)+) => ( Ok() ); - - ($d($arg:tt)+) => ( core::result::Result::<(), ()>::Ok(()) ); - } - - /// Fallible version of `trace!`. - #[cfg(all(any(feature = "log-all", feature = "log-trace"), not(feature = "log-none")))] - #[macro_export(local_inner_macros)] - macro_rules! try_trace { - - (target: $target:expr, $d($arg:tt)+) => ( - $crate::global_try_trace!(target: $target, $d($arg)+) - ); - - ($d($arg:tt)+) => ( - $crate::global_try_trace!($d($arg)+) - ); - } - - /// Fallible version of `trace!`. - #[cfg(not(all(any(feature = "log-all", feature = "log-trace"), not(feature = "log-none"))))] - #[macro_export(local_inner_macros)] - macro_rules! try_trace { - - // (target: $target:expr, $d($arg:tt)+) => ( Ok() ); - - ($d($arg:tt)+) => ( core::result::Result::<(), ()>::Ok(()) ); - } - - /// Fallible version of `warn!`. - #[cfg(all(any(feature = "log-all", feature = "log-warn"), not(feature = "log-none")))] - #[macro_export(local_inner_macros)] - macro_rules! try_warn { - - (target: $target:expr, $d($arg:tt)+) => ( - $crate::global_try_warn!(target: $target, $d($arg)+) - ); - - ($d($arg:tt)+) => ( - $crate::global_try_warn!($d($arg)+) - ); - } - - /// Fallible version of `warn!`. - #[cfg(not(all(any(feature = "log-all", feature = "log-warn"), not(feature = "log-none"))))] - #[macro_export(local_inner_macros)] - macro_rules! try_warn { - - // (target: $target:expr, $d($arg:tt)+) => ( Ok() ); - - ($d($arg:tt)+) => ( core::result::Result::<(), ()>::Ok(()) ); - } - - } - } - } -} diff --git a/src/logger.rs b/src/logger.rs index d8820fe..f6d8036 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -7,7 +7,7 @@ use core::sync::atomic::{AtomicUsize, Ordering}; /// This trait is markes "unsafe" to signal that users should never (need to) "write their own", /// but always go through the `delog!` macro. /// -/// The user has access to the global logger via `delog::trylogger()`, but only as TryLog/Log +/// The user has access to the global logger via `delog::logger()`, but only as TryLog/Log /// implementation, not with this direct access to implementation details. pub unsafe trait Delogger: log::Log + crate::TryLog { @@ -97,11 +97,11 @@ pub trait TryLogWithStatistics: TryLog { #[macro_export] macro_rules! delog { ($logger:ident, $capacity:expr, $flusher:ty) => { - delog!($logger, $capacity, $flusher, renderer: $crate::renderers::ArgumentsRenderer); + delog!($logger, $capacity, $flusher, renderer: $crate::render::DefaultRenderer); impl $logger { - pub fn init_default(level: $crate::upstream::LevelFilter, flusher: &'static $flusher) -> Result<(), ()> { - $logger::init(level, flusher, $crate::renderers::default()) + pub fn init_default(level: $crate::log::LevelFilter, flusher: &'static $flusher) -> Result<(), ()> { + $logger::init(level, flusher, $crate::render::default()) } } }; @@ -120,9 +120,9 @@ macro_rules! delog { unsafe impl Send for $logger {} unsafe impl Sync for $logger {} - impl $crate::upstream::Log for $logger { + impl $crate::log::Log for $logger { /// log level is set via log::set_max_level, not here, hence always true - fn enabled(&self, _: &$crate::upstream::Metadata) -> bool { + fn enabled(&self, _: &$crate::log::Metadata) -> bool { true } @@ -130,21 +130,20 @@ macro_rules! delog { fn flush(&self) { let mut buf = [0u8; $capacity] ; - use $crate::Delogger; let logs: &str = unsafe { $crate::dequeue(*self, &mut buf) }; use $crate::Flusher; self.flusher.flush(logs); } - fn log(&self, record: &$crate::upstream::Record) { + fn log(&self, record: &$crate::log::Record) { // use $crate::Delogger; unsafe { $crate::enqueue(*self, record) } } } impl $crate::TryLog for $logger { - fn try_log(&self, record: &$crate::upstream::Record) -> core::result::Result<(), ()> { + fn try_log(&self, record: &$crate::log::Record) -> core::result::Result<(), ()> { // use $crate::Delogger; unsafe { $crate::try_enqueue(*self, record) } } @@ -172,9 +171,8 @@ macro_rules! delog { #[allow(missing_docs)] impl $logger { - pub fn init(level: $crate::upstream::LevelFilter, flusher: &'static $flusher, renderer: &'static $renderer) -> Result<(), ()> { - use core::sync::atomic::{self, AtomicBool, AtomicUsize, Ordering}; - use core::mem::MaybeUninit; + pub fn init(level: $crate::log::LevelFilter, flusher: &'static $flusher, renderer: &'static $renderer) -> Result<(), ()> { + use core::sync::atomic::{AtomicBool, Ordering}; static INITIALIZED: AtomicBool = AtomicBool::new(false); if INITIALIZED @@ -184,9 +182,9 @@ macro_rules! delog { // let logger = Self { flusher, immediate_flusher: flusher }; let logger = Self { flusher, renderer }; Self::get().replace(logger); - $crate::trylogger().replace(Self::get().as_ref().unwrap()); - $crate::upstream::set_logger(Self::get().as_ref().unwrap()) - .map(|()| $crate::upstream::set_max_level(level)) + $crate::logger().replace(Self::get().as_ref().unwrap()); + $crate::log::set_logger(Self::get().as_ref().unwrap()) + .map(|()| $crate::log::set_max_level(level)) .map_err(|_| ()) } else { Err(()) @@ -198,10 +196,10 @@ macro_rules! delog { unsafe { &mut LOGGER } } - fn flush() { + pub fn flush() { // gracefully degrade if we're not initialized yet if let Some(logger) = Self::get() { - $crate::upstream::Log::flush(logger) + $crate::log::Log::flush(logger) } } } diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 0000000..ad0eeb7 --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,285 @@ +/// Fallible version of `log!`. +#[macro_export] +macro_rules! try_log { + + (target: $target:expr, $lvl:expr, $message:expr) => ({ + let lvl = $lvl; + if lvl <= $crate::log::STATIC_MAX_LEVEL && lvl <= $crate::log::max_level() { + // ensure that $message is a valid format string literal + let _ = $crate::log::__log_format_args!($message); + $crate::__private_api_try_log_lit( + $message, + lvl, + &($target, $crate::log::__log_module_path!(), $crate::log::__log_file!(), $crate::log::__log_line!()), + ) + } else { + Ok(()) + } + }); + + (target: $target:expr, $lvl:expr, $($arg:tt)+) => ({ + let lvl = $lvl; + if lvl <= $crate::log::STATIC_MAX_LEVEL && lvl <= $crate::log::max_level() { + $crate::__private_api_try_log( + $crate::log::__log_format_args!($($arg)+), + lvl, + &($target, $crate::log::__log_module_path!(), $crate::log::__log_file!(), $crate::log::__log_line!()), + ) + } else { + Ok(()) + } + }); + + ($lvl:expr, $($arg:tt)+) => ($crate::try_log!(target: $crate::log::__log_module_path!(), $lvl, $($arg)+)) +} + +// There is a syntax issue with "repetitions in binding patterns for nested macros", +// with a workaround: https://github.com/rust-lang/rust/issues/35853#issuecomment-443110660 +// +// This is why we want to have `($)` expressions in the following, can just imagine they're not there. +// +// Unfortunately, I couldn't get this to work, so instead we use the weird `with_dollar_sign!` instead. + +#[macro_export] +#[doc(hidden)] +macro_rules! with_dollar_sign { + ($($body:tt)*) => { + macro_rules! __with_dollar_sign { $($body)* } + __with_dollar_sign!($); + } +} + +/// Generate logging macros that can be gated by library. +/// +/// Realize that these macros are generated **in the namespace of the consuming library**, the one +/// that actally later makes calls to `local_warn!` etc. +/// +/// To see this in action, compile documentation using `cargo doc --features example`, or inspect +/// the `gate-tests/` subdirectory. +#[macro_export] +macro_rules! generate_macros { + () => { + $crate::with_dollar_sign! { + ($d:tt) => { + + /// Fallible version of `debug!`. + #[cfg(all(any(feature = "log-all", feature = "log-debug"), not(feature = "log-none")))] + #[macro_use] + macro_rules! try_debug { + + (target: $target:expr, $d($arg:tt)+) => ( + $crate::try_log!(target: $target, $crate::Level::Debug, $d($arg)+) + ); + + ($d($arg:tt)+) => ( + $crate::try_log!($crate::Level::Debug, $d($arg)+) + ); + } + + /// Fallible version of `debug!`. + #[cfg(not(all(any(feature = "log-all", feature = "log-debug"), not(feature = "log-none"))))] + #[macro_use] + macro_rules! try_debug { + + // (target: $target:expr, $d($arg:tt)+) => ( core::result::Result::<(), ()>::Ok(()) ); + ($d($arg:tt)+) => ( core::result::Result::<(), ()>::Ok(()) ); + } + + /// Fallible version of `error!`. + #[cfg(all(any(feature = "log-all", feature = "log-error"), not(feature = "log-none")))] + #[macro_use] + macro_rules! try_error { + + (target: $target:expr, $d($arg:tt)+) => ( + $crate::try_log!(target: $target, $crate::Level::Error, $d($arg)+) + ); + + ($d($arg:tt)+) => ( + $crate::try_log!($crate::Level::Error, $d($arg)+) + ); + } + + /// Fallible version of `error!`. + #[cfg(not(all(any(feature = "log-all", feature = "log-error"), not(feature = "log-none"))))] + #[macro_use] + macro_rules! try_error { + + // (target: $target:expr, $d($arg:tt)+) => ( core::result::Result::<(), ()>::Ok(()) ); + ($d($arg:tt)+) => ( core::result::Result::<(), ()>::Ok(()) ); + } + + /// Fallible version of `info!`. + #[cfg(all(any(feature = "log-all", feature = "log-info"), not(feature = "log-none")))] + #[macro_use] + macro_rules! try_info { + + (target: $target:expr, $d($arg:tt)+) => ( + $crate::try_log!(target: $target, $crate::Level::Info, $d($arg)+) + ); + + ($d($arg:tt)+) => ( + $crate::try_log!($crate::Level::Info, $d($arg)+) + ); + } + + /// Fallible version of `info!`. + #[cfg(not(all(any(feature = "log-all", feature = "log-info"), not(feature = "log-none"))))] + #[macro_use] + macro_rules! try_info { + + // (target: $target:expr, $d($arg:tt)+) => ( core::result::Result::<(), ()>::Ok(()) ); + ($d($arg:tt)+) => ( core::result::Result::<(), ()>::Ok(()) ); + } + + /// Fallible version of `trace!`. + #[cfg(all(any(feature = "log-all", feature = "log-trace"), not(feature = "log-none")))] + #[macro_use] + macro_rules! try_trace { + + (target: $target:expr, $d($arg:tt)+) => ( + $crate::try_log!(target: $target, $crate::Level::Trace, $d($arg)+) + ); + + ($d($arg:tt)+) => ( + $crate::try_log!($crate::Level::Trace, $d($arg)+) + ); + } + + /// Fallible version of `trace!`. + #[cfg(not(all(any(feature = "log-all", feature = "log-trace"), not(feature = "log-none"))))] + #[macro_use] + macro_rules! try_trace { + + (target: $target:expr, $d($arg:tt)+) => ( core::result::Result::<(), ()>::Ok(()) ); + ($d($arg:tt)+) => ( core::result::Result::<(), ()>::Ok(()) ); + } + + /// Fallible version of `warn!`. + #[cfg(all(any(feature = "log-all", feature = "log-warn"), not(feature = "log-none")))] + #[macro_use] + macro_rules! try_warn { + + (target: $target:expr, $d($arg:tt)+) => ( + $crate::try_log!(target: $target, $crate::Level::Warn, $d($arg)+) + ); + + ($d($arg:tt)+) => ( + $crate::try_log!($crate::Level::Warn, $d($arg)+) + ); + } + + /// Fallible version of `warn!`. + #[cfg(not(all(any(feature = "log-all", feature = "log-warn"), not(feature = "log-none"))))] + #[macro_use] + macro_rules! try_warn { + + (target: $target:expr, $d($arg:tt)+) => ( core::result::Result::<(), ()>::Ok(()) ); + ($d($arg:tt)+) => ( core::result::Result::<(), ()>::Ok(()) ); + } + + #[cfg(not(feature = "log-none"))] + /// Local version of `log!`. + #[macro_use] + macro_rules! log { + (target: $target:expr, $d($arg:tt)+) => ( $crate::try_log!(target: $target, $d($arg)+).ok() ); + ($d($arg:tt)+) => ( $crate::try_log!($d($arg)+).ok() ); + } + + #[cfg(feature = "log-none")] + /// Local version of `log!`. + #[macro_use] + macro_rules! log { + ($d($arg:tt)+) => ( core::result::Result::<(), ()>::Ok(()) ); + } + + #[macro_use] + /// Local version of `debug!`. + macro_rules! debug { + // (target: $target:expr, $d($arg:tt)+) => ( try_debug!(target: $target, $d($arg)+).ok() ); + ($d($arg:tt)+) => ( try_debug!($d($arg)+).ok() ); + } + + #[macro_use] + /// Local version of `error!`. + macro_rules! error { + // (target: $target:expr, $d($arg:tt)+) => ( try_error!(target: $target, $d($arg)+).ok() ); + ($d($arg:tt)+) => ( try_error!($d($arg)+).ok() ); + } + + #[macro_use] + /// Local version of `info!`. + macro_rules! info { + // (target: $target:expr, $d($arg:tt)+) => ( try_info!(target: $target, $d($arg)+).ok() ); + ($d($arg:tt)+) => ( try_info!($d($arg)+).ok() ); + } + + #[macro_use] + /// Local version of `trace!`. + macro_rules! trace { + // (target: $target:expr, $d($arg:tt)+) => ( $try_trace!(target: $target, $d($arg)+).ok() ); + ($d($arg:tt)+) => ( $try_trace!($d($arg)+).ok() ); + } + + #[macro_use] + /// Local version of `warn!`. + macro_rules! warn { + // (target: $target:expr, $d($arg:tt)+) => ( try_warn!(target: $target, $d($arg)+).ok() ); + ($d($arg:tt)+) => ( try_warn!($d($arg)+).ok() ); + } + + #[macro_use] + /// Immediate version of `log!`. + macro_rules! log_now { + ($lvl:expr, $d($arg:tt)+) => ( log!(target: "!", $lvl, $d($arg)+) ); + } + + #[macro_use] + /// Immediate version of `debug!`. + macro_rules! debug_now { ($d($arg:tt)+) => ( debug!(target: "!", $d($arg)+) ); } + + #[macro_use] + /// Immediate version of `error!`. + macro_rules! error_now { ($d($arg:tt)+) => ( error!(target: "!", $d($arg)+) ); } + + #[macro_use] + /// Immediate version of `info!`. + macro_rules! info_now { ($d($arg:tt)+) => ( info!(target: "!", $d($arg)+) ); } + + #[macro_use] + /// Immediate version of `trace!`. + macro_rules! trace_now { ($d($arg:tt)+) => ( trace!(target: "!", $d($arg)+) ); } + + #[macro_use] + /// Immediate version of `warn!`. + macro_rules! warn_now { ($d($arg:tt)+) => ( warn!(target: "!", $d($arg)+) ); } + + #[macro_use] + /// Fallible immediate version of `log!`. + macro_rules! try_log_now { + ($lvl:expr, $d($arg:tt)+) => ( try_log!(target: "!", $lvl, $d($arg)+) ); + } + + #[macro_use] + /// Fallible immediate version of `debug!`. + macro_rules! try_debug_now { ($d($arg:tt)+) => ( try_debug!(target: "!", $d($arg)+) ); } + + #[macro_use] + /// Fallible immediate version of `error!`. + macro_rules! try_error_now { ($d($arg:tt)+) => ( try_error!(target: "!", $d($arg)+) ); } + + #[macro_use] + /// Fallible immediate version of `info!`. + macro_rules! try_info_now { ($d($arg:tt)+) => ( try_info!(target: "!", $d($arg)+) ); } + + #[macro_use] + /// Fallible immediate version of `trace!`. + macro_rules! try_trace_now { ($d($arg:tt)+) => ( try_trace!(target: "!", $d($arg)+) ); } + + #[macro_use] + /// Fallible immediate version of `warn!`. + macro_rules! try_warn_now { ($d($arg:tt)+) => ( try_warn!(target: "!", $d($arg)+) ); } + + } + } + } +} diff --git a/src/render.rs b/src/render.rs index f7a8c3c..9b1a29e 100644 --- a/src/render.rs +++ b/src/render.rs @@ -1,4 +1,4 @@ -//! A lone helper function to render formatting arguments. +//! The default, minimal renderer, and some helper functions. use core::{cmp, fmt}; @@ -28,7 +28,7 @@ pub fn render_record<'a>(buf: &'a mut [u8], record: &log::Record) -> &'a [u8] { } } -// I dont' get it, why isn't this implemented already? +// I don't get it, why isn't this implemented already? struct WriteTo<'a> { buffer: &'a mut [u8], // on write error (i.e. not enough space in buffer) this grows beyond @@ -65,3 +65,41 @@ impl<'a> core::fmt::Write for WriteTo<'a> { } } +use crate::Renderer; + +#[derive(Clone, Copy)] +/// Renders just the `record.args()`. +pub struct DefaultRenderer {} + +/// The default, minimal renderer. +pub fn default() -> &'static DefaultRenderer { + static RENDERER: DefaultRenderer = DefaultRenderer {}; + &RENDERER +} + + +impl Renderer for DefaultRenderer { + fn render<'a>(&self, buf: &'a mut [u8], record: &log::Record) -> &'a [u8] { + render_arguments(buf, *record.args()) + } +} + +unsafe impl Send for DefaultRenderer {} +unsafe impl Sync for DefaultRenderer {} + +#[derive(Clone, Copy)] +/// Renders the `record.args()`, prefixed by level, target, and file, line if they are some. +pub struct RipgrepRenderer {} + +impl Renderer for RipgrepRenderer { + fn render<'a>(&self, buf: &'a mut [u8], record: &log::Record) -> &'a [u8] { + match (record.file(), record.line()) { + (Some(file), Some(line)) => render_arguments(buf, + format_args!("{}|{}|{}:{}: {}", record.level(), record.target(), file, line, record.args())), + (Some(file), None) => render_arguments(buf, + format_args!("{}|{}|{}: {}", record.level(), record.target(), file, record.args())), + _ => render_arguments(buf, + format_args!("{}|{}: {}", record.level(), record.target(), record.args())), + } + } +} diff --git a/src/renderers.rs b/src/renderers.rs index 28ce569..c7682ee 100644 --- a/src/renderers.rs +++ b/src/renderers.rs @@ -5,23 +5,23 @@ use crate::Renderer; #[derive(Clone, Copy)] /// Renders just the `record.args()`. -pub struct ArgumentsRenderer {} +pub struct DefaultRenderer {} /// The default, minimal renderer. -pub fn default() -> &'static ArgumentsRenderer { - static RENDERER: ArgumentsRenderer = ArgumentsRenderer {}; +pub fn default() -> &'static DefaultRenderer { + static RENDERER: DefaultRenderer = DefaultRenderer {}; &RENDERER } -impl Renderer for ArgumentsRenderer { +impl Renderer for DefaultRenderer { fn render<'a>(&self, buf: &'a mut [u8], record: &log::Record) -> &'a [u8] { render_arguments(buf, *record.args()) } } -unsafe impl Send for ArgumentsRenderer {} -unsafe impl Sync for ArgumentsRenderer {} +unsafe impl Send for DefaultRenderer {} +unsafe impl Sync for DefaultRenderer {} #[derive(Clone, Copy)] /// Renders the `record.args()`, prefixed by level, target, and file, line if they are some.