From 548e34a042396f524c55a07004599480e4e5f686 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=87a=C4=9Fatay=20Yi=C4=9Fit=20=C5=9Eahin?= Date: Thu, 2 Apr 2026 16:36:04 +0200 Subject: [PATCH 1/4] refactor(env_filter): fix unreachable pub warning Co-authored-by: WolverinDEV --- crates/env_filter/src/op.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/env_filter/src/op.rs b/crates/env_filter/src/op.rs index bfc6a5c..453e55a 100644 --- a/crates/env_filter/src/op.rs +++ b/crates/env_filter/src/op.rs @@ -24,13 +24,13 @@ impl FilterOp { #[cfg(not(feature = "regex"))] impl FilterOp { - pub fn new(spec: &str) -> Result { + pub(crate) fn new(spec: &str) -> Result { Ok(Self { inner: spec.to_string(), }) } - pub fn is_match(&self, s: &str) -> bool { + pub(crate) fn is_match(&self, s: &str) -> bool { s.contains(&self.inner) } } From 9c70eafd442e7ec33a6e04cdebd3f50a77af444b Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Fri, 22 Aug 2025 03:05:27 +0200 Subject: [PATCH 2/4] feat(filter): Add support for no_std environments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Çağatay Yiğit Şahin --- crates/env_filter/Cargo.toml | 7 ++++--- crates/env_filter/src/directive.rs | 2 ++ crates/env_filter/src/filter.rs | 12 ++++++------ crates/env_filter/src/lib.rs | 3 +++ crates/env_filter/src/op.rs | 3 ++- crates/env_filter/src/parser.rs | 13 +++++++++---- 6 files changed, 26 insertions(+), 14 deletions(-) diff --git a/crates/env_filter/Cargo.toml b/crates/env_filter/Cargo.toml index 54c1af4..a63e306 100644 --- a/crates/env_filter/Cargo.toml +++ b/crates/env_filter/Cargo.toml @@ -26,11 +26,12 @@ pre-release-replacements = [ ] [features] -default = ["regex"] -regex = ["dep:regex"] +default = ["std", "regex"] +regex = ["std", "dep:regex"] +std = [] [dependencies] -log = { version = "0.4.29", features = ["std"] } +log = { version = "0.4.29", default-features = false } regex = { version = "1.12.3", optional = true, default-features=false, features=["std", "perf"] } [dev-dependencies] diff --git a/crates/env_filter/src/directive.rs b/crates/env_filter/src/directive.rs index c24fef2..77c12ed 100644 --- a/crates/env_filter/src/directive.rs +++ b/crates/env_filter/src/directive.rs @@ -1,3 +1,5 @@ +use alloc::string::String; + use log::Level; use log::LevelFilter; diff --git a/crates/env_filter/src/filter.rs b/crates/env_filter/src/filter.rs index 60bcf7a..5eb7d0a 100644 --- a/crates/env_filter/src/filter.rs +++ b/crates/env_filter/src/filter.rs @@ -1,12 +1,10 @@ -use std::env; -use std::fmt; -use std::mem; +use alloc::{borrow::ToOwned, string::ToString, vec::Vec}; +use core::{fmt, mem}; use log::{LevelFilter, Metadata, Record}; use crate::enabled; use crate::parse_spec; -use crate::parser::ParseResult; use crate::Directive; use crate::FilterOp; use crate::ParseError; @@ -48,10 +46,11 @@ impl Builder { } /// Initializes the filter builder from an environment. + #[cfg(feature = "std")] pub fn from_env(env: &str) -> Builder { let mut builder = Builder::new(); - if let Ok(s) = env::var(env) { + if let Ok(s) = std::env::var(env) { builder.parse(&s); } @@ -98,10 +97,11 @@ impl Builder { /// See the [Enabling Logging] section for more details. /// /// [Enabling Logging]: ../index.html#enabling-logging + #[cfg(feature = "std")] pub fn parse(&mut self, filters: &str) -> &mut Self { #![allow(clippy::print_stderr)] // compatibility - let ParseResult { + let crate::parser::ParseResult { directives, filter, errors, diff --git a/crates/env_filter/src/lib.rs b/crates/env_filter/src/lib.rs index 0b35088..b530f73 100644 --- a/crates/env_filter/src/lib.rs +++ b/crates/env_filter/src/lib.rs @@ -38,10 +38,13 @@ //! ``` #![cfg_attr(docsrs, feature(doc_cfg))] +#![cfg_attr(not(feature = "std"), no_std)] #![warn(missing_docs)] #![warn(clippy::print_stderr)] #![warn(clippy::print_stdout)] +extern crate alloc; + mod directive; mod filter; mod filtered_log; diff --git a/crates/env_filter/src/op.rs b/crates/env_filter/src/op.rs index 453e55a..e805edf 100644 --- a/crates/env_filter/src/op.rs +++ b/crates/env_filter/src/op.rs @@ -1,4 +1,5 @@ -use std::fmt; +use alloc::string::{String, ToString}; +use core::fmt; #[derive(Debug, Clone)] pub(crate) struct FilterOp { diff --git a/crates/env_filter/src/parser.rs b/crates/env_filter/src/parser.rs index 097058d..9938781 100644 --- a/crates/env_filter/src/parser.rs +++ b/crates/env_filter/src/parser.rs @@ -1,6 +1,7 @@ +use alloc::{borrow::ToOwned, format, string::String, vec::Vec}; +use core::fmt::{Display, Formatter}; + use log::LevelFilter; -use std::error::Error; -use std::fmt::{Display, Formatter}; use crate::Directive; use crate::FilterOp; @@ -46,12 +47,16 @@ pub struct ParseError { } impl Display for ParseError { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { write!(f, "error parsing logger filter: {}", self.details) } } -impl Error for ParseError {} +#[cfg(feature = "std")] +impl std::error::Error for ParseError {} + +#[cfg(not(feature = "std"))] +impl core::error::Error for ParseError {} /// Parse a logging specification string (e.g: `crate1,crate2::mod3,crate3::x=error/foo`) /// and return a vector with log directives. From 663c9c22452bb588c4c8e6ee187e659f10132092 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=87a=C4=9Fatay=20Yi=C4=9Fit=20=C5=9Eahin?= Date: Thu, 2 Apr 2026 16:42:47 +0200 Subject: [PATCH 3/4] fix: adapt testing and CI to the newly optional std feature for env_filter Some parts of the tests are skipped with no_std as they require `snapbox::str!` which does not support no_std. --- .github/workflows/ci.yml | 8 ++++- Cargo.toml | 2 +- crates/env_filter/Cargo.toml | 1 - crates/env_filter/src/filter.rs | 16 +++++++--- crates/env_filter/src/parser.rs | 55 +++++++++++++++++++++++---------- 5 files changed, 58 insertions(+), 24 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b575b92..b6650c5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -72,7 +72,13 @@ jobs: - uses: Swatinem/rust-cache@v2 - uses: taiki-e/install-action@cargo-hack - name: Default features - run: cargo hack check --each-feature --locked --rust-version --ignore-private --workspace --all-targets --keep-going + run: + - cargo hack check --each-feature --locked --rust-version --ignore-private --package env_logger --all-targets --keep-going + # When the std feature is enabled, a lower MSRV of 1.71 is sufficient and was the MSRV for the crate before std was made optional. + # To make sure we keep it that way, check configurations with std enabled and disabled separately and with their respective MSRVs. + # "--at-least-one-of" requires "--feature-powerset", which can be made to emulate "--each-feature" with "--depth 1". + - cargo hack check --feature-powerset --depth 1 --locked --version-range 1.71..=1.71 --ignore-private --package env_filter --all-targets --keep-going --at-least-one-of std,default,regex + - cargo hack check --no-default-features --locked --version-range 1.81..=1.81 --ignore-private --package env_filter --all-targets --keep-going minimal-versions: name: Minimal versions strategy: diff --git a/Cargo.toml b/Cargo.toml index 76b92e7..ea46b6d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -135,7 +135,7 @@ unstable-kv = ["kv"] [dependencies] log = { version = "0.4.29", features = ["std"] } -env_filter = { version = "1.0.0", path = "crates/env_filter", default-features = false } +env_filter = { version = "1.0.0", path = "crates/env_filter", default-features = false, features = ["std"] } jiff = { version = "0.2.22", default-features = false, features = ["std"], optional = true } anstream = { version = "1.0.0", default-features = false, features = ["wincon"], optional = true } anstyle = { version = "1.0.13", optional = true } diff --git a/crates/env_filter/Cargo.toml b/crates/env_filter/Cargo.toml index a63e306..f2eadd4 100644 --- a/crates/env_filter/Cargo.toml +++ b/crates/env_filter/Cargo.toml @@ -9,7 +9,6 @@ keywords = ["logging", "log", "logger"] repository.workspace = true license.workspace = true edition.workspace = true -rust-version.workspace = true include.workspace = true [package.metadata.docs.rs] diff --git a/crates/env_filter/src/filter.rs b/crates/env_filter/src/filter.rs index 5eb7d0a..ca2dfe2 100644 --- a/crates/env_filter/src/filter.rs +++ b/crates/env_filter/src/filter.rs @@ -5,6 +5,7 @@ use log::{LevelFilter, Metadata, Record}; use crate::enabled; use crate::parse_spec; +use crate::parser::ParseResult; use crate::Directive; use crate::FilterOp; use crate::ParseError; @@ -97,18 +98,20 @@ impl Builder { /// See the [Enabling Logging] section for more details. /// /// [Enabling Logging]: ../index.html#enabling-logging - #[cfg(feature = "std")] pub fn parse(&mut self, filters: &str) -> &mut Self { #![allow(clippy::print_stderr)] // compatibility - let crate::parser::ParseResult { + let ParseResult { directives, filter, errors, } = parse_spec(filters); for error in errors { + #[cfg(feature = "std")] eprintln!("warning: {error}, ignoring it"); + #[cfg(not(feature = "std"))] + log::warn!("{error}, ignoring it"); } self.filter = filter; @@ -258,8 +261,9 @@ impl fmt::Debug for Filter { #[cfg(test)] mod tests { + use alloc::{borrow::ToOwned, vec, vec::Vec}; + use log::{Level, LevelFilter}; - use snapbox::{assert_data_eq, str}; use super::{enabled, Builder, Directive, Filter}; @@ -486,10 +490,12 @@ mod tests { #[test] fn try_parse_invalid_filter() { + #[allow(unused_variables)] let error = Builder::new().try_parse("info,crate1=invalid").unwrap_err(); - assert_data_eq!( + #[cfg(feature = "std")] + snapbox::assert_data_eq!( error, - str!["error parsing logger filter: invalid logging spec 'invalid'"] + snapbox::str!["error parsing logger filter: invalid logging spec 'invalid'"] ); } diff --git a/crates/env_filter/src/parser.rs b/crates/env_filter/src/parser.rs index 9938781..2da1054 100644 --- a/crates/env_filter/src/parser.rs +++ b/crates/env_filter/src/parser.rs @@ -120,9 +120,13 @@ pub(crate) fn parse_spec(spec: &str) -> ParseResult { #[cfg(test)] mod tests { + use alloc::{borrow::ToOwned, string::ToString}; + use crate::ParseError; use log::LevelFilter; - use snapbox::{assert_data_eq, str, Data, IntoData}; + #[cfg(feature = "std")] + use snapbox::{assert_data_eq, str}; + use snapbox::{Data, IntoData}; use super::{parse_spec, ParseResult}; @@ -169,6 +173,7 @@ mod tests { assert!(filter.is_none()); assert_eq!(errors.len(), 1); + #[cfg(feature = "std")] assert_data_eq!( &errors[0], str!["invalid logging spec 'crate1::mod1=warn=info'"] @@ -190,6 +195,7 @@ mod tests { assert!(filter.is_none()); assert_eq!(errors.len(), 1); + #[cfg(feature = "std")] assert_data_eq!(&errors[0], str!["invalid logging spec 'noNumber'"]); } @@ -208,6 +214,7 @@ mod tests { assert!(filter.is_none()); assert_eq!(errors.len(), 1); + #[cfg(feature = "std")] assert_data_eq!(&errors[0], str!["invalid logging spec 'wrong'"]); } @@ -226,6 +233,7 @@ mod tests { assert!(filter.is_none()); assert_eq!(errors.len(), 1); + #[cfg(feature = "std")] assert_data_eq!(&errors[0], str!["invalid logging spec 'wrong'"]); } @@ -399,6 +407,7 @@ mod tests { assert!(filter.is_some() && filter.unwrap().to_string() == "a.c"); assert_eq!(errors.len(), 1); + #[cfg(feature = "std")] assert_data_eq!( &errors[0], str!["invalid logging spec 'crate1::mod1=error=warn'"] @@ -430,6 +439,7 @@ mod tests { assert!(filter.is_none()); assert_eq!(errors.len(), 1); + #[cfg(feature = "std")] assert_data_eq!( &errors[0], str!["invalid logging spec 'debug/abc/a.c' (too many '/'s)"] @@ -451,14 +461,17 @@ mod tests { assert!(filter.is_none()); assert_eq!(errors.len(), 2); - assert_data_eq!( - &errors[0], - str!["invalid logging spec 'crate1::mod1=warn=info'"] - ); - assert_data_eq!( - &errors[1], - str!["invalid logging spec 'crate3=error=error'"] - ); + #[cfg(feature = "std")] + { + assert_data_eq!( + &errors[0], + str!["invalid logging spec 'crate1::mod1=warn=info'"] + ); + assert_data_eq!( + &errors[1], + str!["invalid logging spec 'crate3=error=error'"] + ); + } } #[test] @@ -476,8 +489,11 @@ mod tests { assert!(filter.is_none()); assert_eq!(errors.len(), 2); - assert_data_eq!(&errors[0], str!["invalid logging spec 'noNumber'"]); - assert_data_eq!(&errors[1], str!["invalid logging spec 'invalid'"]); + #[cfg(feature = "std")] + { + assert_data_eq!(&errors[0], str!["invalid logging spec 'noNumber'"]); + assert_data_eq!(&errors[1], str!["invalid logging spec 'invalid'"]); + } } #[test] @@ -495,18 +511,23 @@ mod tests { assert!(filter.is_none()); assert_eq!(errors.len(), 2); - assert_data_eq!( - &errors[0], - str!["invalid logging spec 'crate1::mod1=debug=info'"] - ); - assert_data_eq!(&errors[1], str!["invalid logging spec 'invalid'"]); + #[cfg(feature = "std")] + { + assert_data_eq!( + &errors[0], + str!["invalid logging spec 'crate1::mod1=debug=info'"] + ); + assert_data_eq!(&errors[1], str!["invalid logging spec 'invalid'"]); + } } #[test] fn parse_error_message_single_error() { + #[allow(unused_variables)] let error = parse_spec("crate1::mod1=debug=info,crate2=debug") .ok() .unwrap_err(); + #[cfg(feature = "std")] assert_data_eq!( error, str!["error parsing logger filter: invalid logging spec 'crate1::mod1=debug=info'"] @@ -515,9 +536,11 @@ mod tests { #[test] fn parse_error_message_multiple_errors() { + #[allow(unused_variables)] let error = parse_spec("crate1::mod1=debug=info,crate2=debug,crate3=invalid") .ok() .unwrap_err(); + #[cfg(feature = "std")] assert_data_eq!( error, str!["error parsing logger filter: invalid logging spec 'crate1::mod1=debug=info'"] From b754c66235c7ef0e8af03954b2816e93f9fdb709 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=87a=C4=9Fatay=20Yi=C4=9Fit=20=C5=9Eahin?= Date: Thu, 2 Apr 2026 20:38:41 +0200 Subject: [PATCH 4/4] feat(env_filter): make the regex feature compatible with no_std --- .github/workflows/ci.yml | 5 ++--- crates/env_filter/Cargo.toml | 6 +++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b6650c5..ab29998 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -76,9 +76,8 @@ jobs: - cargo hack check --each-feature --locked --rust-version --ignore-private --package env_logger --all-targets --keep-going # When the std feature is enabled, a lower MSRV of 1.71 is sufficient and was the MSRV for the crate before std was made optional. # To make sure we keep it that way, check configurations with std enabled and disabled separately and with their respective MSRVs. - # "--at-least-one-of" requires "--feature-powerset", which can be made to emulate "--each-feature" with "--depth 1". - - cargo hack check --feature-powerset --depth 1 --locked --version-range 1.71..=1.71 --ignore-private --package env_filter --all-targets --keep-going --at-least-one-of std,default,regex - - cargo hack check --no-default-features --locked --version-range 1.81..=1.81 --ignore-private --package env_filter --all-targets --keep-going + - cargo hack check --feature-powerset --depth 2 --locked --version-range 1.71..=1.71 --ignore-private --package env_filter --all-targets --keep-going --at-least-one-of std,default + - cargo hack check --each-feature --locked --version-range 1.81..=1.81 --ignore-private --package env_filter --all-targets --keep-going --exclude-features std,default minimal-versions: name: Minimal versions strategy: diff --git a/crates/env_filter/Cargo.toml b/crates/env_filter/Cargo.toml index f2eadd4..54d5780 100644 --- a/crates/env_filter/Cargo.toml +++ b/crates/env_filter/Cargo.toml @@ -26,12 +26,12 @@ pre-release-replacements = [ [features] default = ["std", "regex"] -regex = ["std", "dep:regex"] -std = [] +regex = ["dep:regex"] +std = ["regex/std"] [dependencies] log = { version = "0.4.29", default-features = false } -regex = { version = "1.12.3", optional = true, default-features=false, features=["std", "perf"] } +regex = { version = "1.12.3", optional = true, default-features=false, features=["perf"] } [dev-dependencies] snapbox = "1.0"