diff --git a/.clippy.toml b/.clippy.toml index 18554b15..027eef41 100644 --- a/.clippy.toml +++ b/.clippy.toml @@ -1,5 +1,8 @@ -msrv = "1.71" # MSRV warn-on-all-wildcard-imports = true +allow-print-in-tests = true +allow-expect-in-tests = true +allow-unwrap-in-tests = true +allow-dbg-in-tests = true disallowed-methods = [ { path = "std::option::Option::map_or", reason = "prefer `map(..).unwrap_or(..)` for legibility" }, { path = "std::option::Option::map_or_else", reason = "prefer `map(..).unwrap_or_else(..)` for legibility" }, diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 3fdb4a41..436ba208 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -3,6 +3,7 @@ 'before 5am on the first day of the month', ], semanticCommits: 'enabled', + commitMessageLowerCase: 'never', configMigration: true, dependencyDashboard: true, customManagers: [ @@ -20,7 +21,25 @@ 'MSRV.*?(?\\d+\\.\\d+(\\.\\d+)?)', '(?\\d+\\.\\d+(\\.\\d+)?).*?MSRV', ], - depNameTemplate: 'rust', + depNameTemplate: 'MSRV', + packageNameTemplate: 'rust-lang/rust', + datasourceTemplate: 'github-releases', + }, + { + customType: 'regex', + fileMatch: [ + '^rust-toolchain\\.toml$', + 'Cargo.toml$', + 'clippy.toml$', + '\\.clippy.toml$', + '^\\.github/workflows/ci.yml$', + '^\\.github/workflows/rust-next.yml$', + ], + matchStrings: [ + 'STABLE.*?(?\\d+\\.\\d+(\\.\\d+)?)', + '(?\\d+\\.\\d+(\\.\\d+)?).*?STABLE', + ], + depNameTemplate: 'STABLE', packageNameTemplate: 'rust-lang/rust', datasourceTemplate: 'github-releases', }, @@ -32,7 +51,7 @@ 'custom.regex', ], matchPackageNames: [ - 'rust', + 'MSRV', ], minimumReleaseAge: '840 days', // 20 releases * 6 weeks per release * 7 days per week internalChecksFilter: 'strict', @@ -41,6 +60,19 @@ '* * * * *', ], }, + { + commitMessageTopic: 'Rust Stable', + matchManagers: [ + 'custom.regex', + ], + matchPackageNames: [ + 'STABLE', + ], + extractVersion: '^(?\\d+\\.\\d+)', // Drop the patch version + schedule: [ + '* * * * *', + ], + }, // Goals: // - Keep version reqs low, ignoring compatible normal/build dependencies // - Take advantage of latest dev-dependencies @@ -72,6 +104,7 @@ matchCurrentVersion: '>=1.0.0', matchUpdateTypes: [ 'minor', + 'patch', ], enabled: false, }, @@ -99,6 +132,7 @@ matchCurrentVersion: '>=1.0.0', matchUpdateTypes: [ 'minor', + 'patch', ], automerge: true, groupName: 'compatible (dev)', diff --git a/.github/settings.yml b/.github/settings.yml index 6c1efa04..2394b189 100644 --- a/.github/settings.yml +++ b/.github/settings.yml @@ -24,14 +24,18 @@ repository: squash_merge_commit_message: "PR_BODY" merge_commit_message: "PR_BODY" -branches: - - name: main - protection: - required_pull_request_reviews: null - required_conversation_resolution: true - required_status_checks: - # Required. Require branches to be up to date before merging. - strict: false - contexts: ["CI", "Lint Commits", "Spell Check with Typos"] - enforce_admins: false - restrictions: null +# This serves more as documentation. +# Branch protection API was replaced by rulesets but settings isn't updated. +# See https://github.com/repository-settings/app/issues/825 +# +# branches: +# - name: main +# protection: +# required_pull_request_reviews: null +# required_conversation_resolution: true +# required_status_checks: +# # Required. Require branches to be up to date before merging. +# strict: false +# contexts: ["CI", "Spell Check with Typos"] +# enforce_admins: false +# restrictions: null diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml index ccee1fef..07c70eeb 100644 --- a/.github/workflows/audit.yml +++ b/.github/workflows/audit.yml @@ -17,6 +17,10 @@ env: CARGO_TERM_COLOR: always CLICOLOR: 1 +concurrency: + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: true + jobs: security_audit: permissions: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 49e64dfc..f39de032 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,21 +14,27 @@ env: CARGO_TERM_COLOR: always CLICOLOR: 1 +concurrency: + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: true + jobs: ci: permissions: contents: none name: CI - needs: [test, msrv, docs, rustfmt, clippy] + needs: [test, msrv, lockfile, docs, rustfmt, clippy] runs-on: ubuntu-latest + if: "always()" steps: - - name: Done - run: exit 0 + - name: Failed + run: exit 1 + if: "contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') || contains(needs.*.result, 'skipped')" test: name: Test strategy: matrix: - os: ["ubuntu-latest", "windows-latest", "macos-latest"] + os: ["ubuntu-latest", "windows-latest", "macos-14"] rust: ["stable"] continue-on-error: ${{ matrix.rust != 'stable' }} runs-on: ${{ matrix.os }} @@ -42,13 +48,13 @@ jobs: - uses: Swatinem/rust-cache@v2 - uses: taiki-e/install-action@cargo-hack - name: Build - run: cargo test --no-run --workspace --all-features + run: cargo test --workspace --no-run - name: Test - run: cargo hack test --workspace --feature-powerset + run: cargo hack test --feature-powerset --workspace - name: Run crate example run: cargo run --example default msrv: - name: "Check MSRV: 1.71" + name: "Check MSRV" runs-on: ubuntu-latest steps: - name: Checkout repository @@ -56,11 +62,11 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: "1.71" # MSRV + toolchain: stable - uses: Swatinem/rust-cache@v2 - uses: taiki-e/install-action@cargo-hack - - name: Check - run: cargo hack check --workspace --all-targets --feature-powerset + - name: Default features + run: cargo hack check --feature-powerset --locked --rust-version --ignore-private --workspace --all-targets lockfile: runs-on: ubuntu-latest steps: @@ -72,7 +78,7 @@ jobs: toolchain: stable - uses: Swatinem/rust-cache@v2 - name: "Is lockfile updated?" - run: cargo fetch --locked + run: cargo update --workspace --locked docs: name: Docs runs-on: ubuntu-latest @@ -82,7 +88,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: stable + toolchain: "1.76" # STABLE - uses: Swatinem/rust-cache@v2 - name: Check documentation env: @@ -97,9 +103,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - # Not MSRV because its harder to jump between versions and people are - # more likely to have stable - toolchain: stable + toolchain: "1.76" # STABLE components: rustfmt - uses: Swatinem/rust-cache@v2 - name: Check formatting @@ -115,13 +119,13 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: "1.71" # MSRV + toolchain: "1.76" # STABLE components: clippy - uses: Swatinem/rust-cache@v2 - name: Install SARIF tools - run: cargo install clippy-sarif --version 0.3.4 --locked # Held back due to msrv + run: cargo install clippy-sarif --locked - name: Install SARIF tools - run: cargo install sarif-fmt --version 0.3.4 --locked # Held back due to msrv + run: cargo install sarif-fmt --locked - name: Check run: > cargo clippy --workspace --all-features --all-targets --message-format=json -- -D warnings --allow deprecated @@ -136,3 +140,22 @@ jobs: wait-for-processing: true - name: Report status run: cargo clippy --workspace --all-features --all-targets -- -D warnings --allow deprecated + coverage: + name: Coverage + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + - uses: Swatinem/rust-cache@v2 + - name: Install cargo-tarpaulin + run: cargo install cargo-tarpaulin + - name: Gather coverage + run: cargo tarpaulin --output-dir coverage --out lcov + - name: Publish to Coveralls + uses: coverallsapp/github-action@master + with: + github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/committed.yml b/.github/workflows/committed.yml index 04625584..e7a50fbb 100644 --- a/.github/workflows/committed.yml +++ b/.github/workflows/committed.yml @@ -11,6 +11,10 @@ env: CARGO_TERM_COLOR: always CLICOLOR: 1 +concurrency: + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: true + jobs: committed: name: Lint Commits diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 6eb6ca19..1b000abf 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -12,6 +12,10 @@ env: CARGO_TERM_COLOR: always CLICOLOR: 1 +concurrency: + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: true + jobs: pre-commit: permissions: diff --git a/.github/workflows/rust-next.yml b/.github/workflows/rust-next.yml index e9ae9a86..da2a6003 100644 --- a/.github/workflows/rust-next.yml +++ b/.github/workflows/rust-next.yml @@ -12,12 +12,16 @@ env: CARGO_TERM_COLOR: always CLICOLOR: 1 +concurrency: + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: true + jobs: test: name: Test strategy: matrix: - os: ["ubuntu-latest", "windows-latest", "macos-latest"] + os: ["ubuntu-latest", "windows-latest", "macos-latest", "macos-14"] rust: ["stable", "beta"] include: - os: ubuntu-latest @@ -34,9 +38,9 @@ jobs: - uses: Swatinem/rust-cache@v2 - uses: taiki-e/install-action@cargo-hack - name: Build - run: cargo test --no-run --workspace --all-features + run: cargo test --workspace --no-run - name: Test - run: cargo hack test --workspace --feature-powerset + run: cargo hack test --feature-powerset --workspace - name: Run crate example run: cargo run --example default latest: @@ -54,8 +58,8 @@ jobs: - name: Update dependencues run: cargo update - name: Build - run: cargo test --no-run --workspace --all-features + run: cargo test --workspace --no-run - name: Test - run: cargo hack test --workspace --feature-powerset + run: cargo hack test --feature-powerset --workspace - name: Run crate example run: cargo run --example default diff --git a/.github/workflows/spelling.yml b/.github/workflows/spelling.yml index 12f75859..8e58d9ec 100644 --- a/.github/workflows/spelling.yml +++ b/.github/workflows/spelling.yml @@ -10,6 +10,10 @@ env: CARGO_TERM_COLOR: always CLICOLOR: 1 +concurrency: + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: true + jobs: spelling: name: Spell Check with Typos diff --git a/Cargo.toml b/Cargo.toml index 8eb6cd03..f3e37070 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,80 @@ include = [ "tests/**/*", ] +[workspace.lints.rust] +rust_2018_idioms = "warn" +unreachable_pub = "warn" +unsafe_op_in_unsafe_fn = "warn" +unused_lifetimes = "warn" +unused_macro_rules = "warn" +unused_qualifications = "warn" + +[workspace.lints.clippy] +bool_assert_comparison = "allow" +branches_sharing_code = "allow" +checked_conversions = "warn" +collapsible_else_if = "allow" +create_dir = "warn" +dbg_macro = "warn" +debug_assert_with_mut_call = "warn" +doc_markdown = "warn" +empty_enum = "warn" +enum_glob_use = "warn" +exhaustive_enums = "warn" +exhaustive_structs = "warn" +exit = "warn" +expl_impl_clone_on_copy = "warn" +explicit_deref_methods = "warn" +explicit_into_iter_loop = "warn" +fallible_impl_from = "warn" +filter_map_next = "warn" +flat_map_option = "warn" +float_cmp_const = "warn" +fn_params_excessive_bools = "warn" +from_iter_instead_of_collect = "warn" +if_same_then_else = "allow" +implicit_clone = "warn" +imprecise_flops = "warn" +inconsistent_struct_constructor = "warn" +inefficient_to_string = "warn" +infinite_loop = "warn" +invalid_upcast_comparisons = "warn" +items_after_statements = "warn" +large_digit_groups = "warn" +large_stack_arrays = "warn" +large_types_passed_by_value = "warn" +let_and_return = "allow" # sometimes good to name what you are returning +linkedlist = "warn" +lossy_float_literal = "warn" +macro_use_imports = "warn" +match_wildcard_for_single_variants = "warn" +mem_forget = "warn" +mutex_integer = "warn" +needless_continue = "warn" +needless_for_each = "warn" +negative_feature_names = "warn" +path_buf_push_overwrite = "warn" +ptr_as_ptr = "warn" +rc_mutex = "warn" +redundant_feature_names = "warn" +ref_option_ref = "warn" +rest_pat_in_fully_bound_structs = "warn" +same_functions_in_if_condition = "warn" +self_named_module_files = "warn" +semicolon_if_nothing_returned = "warn" +single_match_else = "warn" +str_to_string = "warn" +string_add = "warn" +string_add_assign = "warn" +string_lit_as_bytes = "warn" +string_to_string = "warn" +todo = "warn" +trait_duplication_in_bounds = "warn" +unwrap_used = "warn" +verbose_file_reads = "warn" +wildcard_imports = "warn" +zero_sized_map_values = "warn" + [package] name = "env_logger" version = "0.11.3" @@ -76,3 +150,6 @@ harness = false [[test]] name = "init-twice-retains-filter" harness = false + +[lints] +workspace = true diff --git a/crates/env_filter/Cargo.toml b/crates/env_filter/Cargo.toml index 9ac0fa47..94e864c9 100644 --- a/crates/env_filter/Cargo.toml +++ b/crates/env_filter/Cargo.toml @@ -32,3 +32,6 @@ regex = ["dep:regex"] [dependencies] log = { version = "0.4.8", features = ["std"] } regex = { version = "1.0.3", optional = true, default-features=false, features=["std", "perf"] } + +[lints] +workspace = true diff --git a/crates/env_filter/src/filter.rs b/crates/env_filter/src/filter.rs index c5d107da..5ae018d9 100644 --- a/crates/env_filter/src/filter.rs +++ b/crates/env_filter/src/filter.rs @@ -85,7 +85,7 @@ impl Builder { /// If no module is provided then the filter will apply to all log messages. pub fn filter(&mut self, module: Option<&str>, level: LevelFilter) -> &mut Self { self.insert_directive(Directive { - name: module.map(|s| s.to_string()), + name: module.map(|s| s.to_owned()), level, }); self @@ -145,7 +145,7 @@ impl Default for Builder { } impl fmt::Debug for Builder { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.built { f.debug_struct("Filter").field("built", &true).finish() } else { @@ -195,7 +195,7 @@ impl Filter { } /// Checks if this record matches the configured filter. - pub fn matches(&self, record: &Record) -> bool { + pub fn matches(&self, record: &Record<'_>) -> bool { if !self.enabled(record.metadata()) { return false; } @@ -210,7 +210,7 @@ impl Filter { } /// Determines if a log message with the specified metadata would be logged. - pub fn enabled(&self, metadata: &Metadata) -> bool { + pub fn enabled(&self, metadata: &Metadata<'_>) -> bool { let level = metadata.level(); let target = metadata.target(); @@ -219,7 +219,7 @@ impl Filter { } impl fmt::Debug for Filter { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Filter") .field("filter", &self.filter) .field("directives", &self.directives) @@ -448,11 +448,11 @@ mod tests { fn match_full_path() { let logger = make_logger_filter(vec![ Directive { - name: Some("crate2".to_string()), + name: Some("crate2".to_owned()), level: LevelFilter::Info, }, Directive { - name: Some("crate1::mod1".to_string()), + name: Some("crate1::mod1".to_owned()), level: LevelFilter::Warn, }, ]); @@ -466,11 +466,11 @@ mod tests { fn no_match() { let logger = make_logger_filter(vec![ Directive { - name: Some("crate2".to_string()), + name: Some("crate2".to_owned()), level: LevelFilter::Info, }, Directive { - name: Some("crate1::mod1".to_string()), + name: Some("crate1::mod1".to_owned()), level: LevelFilter::Warn, }, ]); @@ -481,11 +481,11 @@ mod tests { fn match_beginning() { let logger = make_logger_filter(vec![ Directive { - name: Some("crate2".to_string()), + name: Some("crate2".to_owned()), level: LevelFilter::Info, }, Directive { - name: Some("crate1::mod1".to_string()), + name: Some("crate1::mod1".to_owned()), level: LevelFilter::Warn, }, ]); @@ -496,15 +496,15 @@ mod tests { fn match_beginning_longest_match() { let logger = make_logger_filter(vec![ Directive { - name: Some("crate2".to_string()), + name: Some("crate2".to_owned()), level: LevelFilter::Info, }, Directive { - name: Some("crate2::mod".to_string()), + name: Some("crate2::mod".to_owned()), level: LevelFilter::Debug, }, Directive { - name: Some("crate1::mod1".to_string()), + name: Some("crate1::mod1".to_owned()), level: LevelFilter::Warn, }, ]); @@ -520,7 +520,7 @@ mod tests { level: LevelFilter::Info, }, Directive { - name: Some("crate1::mod1".to_string()), + name: Some("crate1::mod1".to_owned()), level: LevelFilter::Warn, }, ]); @@ -536,7 +536,7 @@ mod tests { level: LevelFilter::Info, }, Directive { - name: Some("crate1::mod1".to_string()), + name: Some("crate1::mod1".to_owned()), level: LevelFilter::Off, }, ]); diff --git a/crates/env_filter/src/filtered_log.rs b/crates/env_filter/src/filtered_log.rs index f707bcc3..d72d44bc 100644 --- a/crates/env_filter/src/filtered_log.rs +++ b/crates/env_filter/src/filtered_log.rs @@ -23,16 +23,16 @@ impl Log for FilteredLog { /// Determines if a log message with the specified metadata would be logged. /// /// For the wrapped log, this returns `true` only if both the filter and the wrapped log return `true`. - fn enabled(&self, metadata: &log::Metadata) -> bool { + fn enabled(&self, metadata: &log::Metadata<'_>) -> bool { self.filter.enabled(metadata) && self.log.enabled(metadata) } /// Logs the record. /// /// Forwards the record to the wrapped log, but only if the record matches the filter. - fn log(&self, record: &log::Record) { + fn log(&self, record: &log::Record<'_>) { if self.filter.matches(record) { - self.log.log(record) + self.log.log(record); } } @@ -40,6 +40,6 @@ impl Log for FilteredLog { /// /// Forwards directly to the wrapped log. fn flush(&self) { - self.log.flush() + self.log.flush(); } } diff --git a/crates/env_filter/src/lib.rs b/crates/env_filter/src/lib.rs index dad06817..6a722045 100644 --- a/crates/env_filter/src/lib.rs +++ b/crates/env_filter/src/lib.rs @@ -37,6 +37,11 @@ //! let logger = env_filter::FilteredLog::new(PrintLogger, builder.build()); //! ``` +#![cfg_attr(docsrs, feature(doc_auto_cfg))] +#![warn(missing_docs)] +#![warn(clippy::print_stderr)] +#![warn(clippy::print_stdout)] + mod directive; mod filter; mod filtered_log; diff --git a/crates/env_filter/src/op.rs b/crates/env_filter/src/op.rs index e018e540..fc10f7d5 100644 --- a/crates/env_filter/src/op.rs +++ b/crates/env_filter/src/op.rs @@ -1,7 +1,7 @@ use std::fmt; #[derive(Debug)] -pub struct FilterOp { +pub(crate) struct FilterOp { #[cfg(feature = "regex")] inner: regex::Regex, #[cfg(not(feature = "regex"))] @@ -10,14 +10,14 @@ pub struct FilterOp { #[cfg(feature = "regex")] impl FilterOp { - pub fn new(spec: &str) -> Result { + pub(crate) fn new(spec: &str) -> Result { match regex::Regex::new(spec) { Ok(r) => Ok(Self { inner: r }), Err(e) => Err(e.to_string()), } } - pub fn is_match(&self, s: &str) -> bool { + pub(crate) fn is_match(&self, s: &str) -> bool { self.inner.is_match(s) } } @@ -36,7 +36,7 @@ impl FilterOp { } impl fmt::Display for FilterOp { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.inner.fmt(f) } } diff --git a/crates/env_filter/src/parser.rs b/crates/env_filter/src/parser.rs index 2d0f90b0..3cb01be5 100644 --- a/crates/env_filter/src/parser.rs +++ b/crates/env_filter/src/parser.rs @@ -3,9 +3,11 @@ use log::LevelFilter; use crate::Directive; use crate::FilterOp; -/// Parse a logging specification string (e.g: "crate1,crate2::mod3,crate3::x=error/foo") +/// Parse a logging specification string (e.g: `crate1,crate2::mod3,crate3::x=error/foo`) /// and return a vector with log directives. pub(crate) fn parse_spec(spec: &str) -> (Vec, Option) { + #![allow(clippy::print_stderr)] // compatibility + let mut dirs = Vec::new(); let mut parts = spec.split('/'); @@ -36,9 +38,10 @@ pub(crate) fn parse_spec(spec: &str) -> (Vec, Option) { } } (Some(part0), Some(""), None) => (LevelFilter::max(), Some(part0)), - (Some(part0), Some(part1), None) => match part1.parse() { - Ok(num) => (num, Some(part0)), - _ => { + (Some(part0), Some(part1), None) => { + if let Ok(num) = part1.parse() { + (num, Some(part0)) + } else { eprintln!( "warning: invalid logging spec '{}', \ ignoring it", @@ -46,7 +49,7 @@ pub(crate) fn parse_spec(spec: &str) -> (Vec, Option) { ); continue; } - }, + } _ => { eprintln!( "warning: invalid logging spec '{}', \ @@ -57,7 +60,7 @@ pub(crate) fn parse_spec(spec: &str) -> (Vec, Option) { } }; dirs.push(Directive { - name: name.map(|s| s.to_string()), + name: name.map(|s| s.to_owned()), level: log_level, }); } @@ -84,13 +87,13 @@ mod tests { fn parse_spec_valid() { let (dirs, filter) = parse_spec("crate1::mod1=error,crate1::mod2,crate2=debug"); assert_eq!(dirs.len(), 3); - assert_eq!(dirs[0].name, Some("crate1::mod1".to_string())); + assert_eq!(dirs[0].name, Some("crate1::mod1".to_owned())); assert_eq!(dirs[0].level, LevelFilter::Error); - assert_eq!(dirs[1].name, Some("crate1::mod2".to_string())); + assert_eq!(dirs[1].name, Some("crate1::mod2".to_owned())); assert_eq!(dirs[1].level, LevelFilter::max()); - assert_eq!(dirs[2].name, Some("crate2".to_string())); + assert_eq!(dirs[2].name, Some("crate2".to_owned())); assert_eq!(dirs[2].level, LevelFilter::Debug); assert!(filter.is_none()); } @@ -100,7 +103,7 @@ mod tests { // test parse_spec with multiple = in specification let (dirs, filter) = parse_spec("crate1::mod1=warn=info,crate2=debug"); assert_eq!(dirs.len(), 1); - assert_eq!(dirs[0].name, Some("crate2".to_string())); + assert_eq!(dirs[0].name, Some("crate2".to_owned())); assert_eq!(dirs[0].level, LevelFilter::Debug); assert!(filter.is_none()); } @@ -110,7 +113,7 @@ mod tests { // test parse_spec with 'noNumber' as log level let (dirs, filter) = parse_spec("crate1::mod1=noNumber,crate2=debug"); assert_eq!(dirs.len(), 1); - assert_eq!(dirs[0].name, Some("crate2".to_string())); + assert_eq!(dirs[0].name, Some("crate2".to_owned())); assert_eq!(dirs[0].level, LevelFilter::Debug); assert!(filter.is_none()); } @@ -120,7 +123,7 @@ mod tests { // test parse_spec with 'warn' as log level let (dirs, filter) = parse_spec("crate1::mod1=wrong,crate2=warn"); assert_eq!(dirs.len(), 1); - assert_eq!(dirs[0].name, Some("crate2".to_string())); + assert_eq!(dirs[0].name, Some("crate2".to_owned())); assert_eq!(dirs[0].level, LevelFilter::Warn); assert!(filter.is_none()); } @@ -130,7 +133,7 @@ mod tests { // test parse_spec with '' as log level let (dirs, filter) = parse_spec("crate1::mod1=wrong,crate2="); assert_eq!(dirs.len(), 1); - assert_eq!(dirs[0].name, Some("crate2".to_string())); + assert_eq!(dirs[0].name, Some("crate2".to_owned())); assert_eq!(dirs[0].level, LevelFilter::max()); assert!(filter.is_none()); } @@ -191,7 +194,7 @@ mod tests { assert_eq!(dirs.len(), 2); assert_eq!(dirs[0].name, None); assert_eq!(dirs[0].level, LevelFilter::Warn); - assert_eq!(dirs[1].name, Some("crate2".to_string())); + assert_eq!(dirs[1].name, Some("crate2".to_owned())); assert_eq!(dirs[1].level, LevelFilter::Debug); assert!(filter.is_none()); } @@ -230,13 +233,13 @@ mod tests { fn parse_spec_valid_filter() { let (dirs, filter) = parse_spec("crate1::mod1=error,crate1::mod2,crate2=debug/abc"); assert_eq!(dirs.len(), 3); - assert_eq!(dirs[0].name, Some("crate1::mod1".to_string())); + assert_eq!(dirs[0].name, Some("crate1::mod1".to_owned())); assert_eq!(dirs[0].level, LevelFilter::Error); - assert_eq!(dirs[1].name, Some("crate1::mod2".to_string())); + assert_eq!(dirs[1].name, Some("crate1::mod2".to_owned())); assert_eq!(dirs[1].level, LevelFilter::max()); - assert_eq!(dirs[2].name, Some("crate2".to_string())); + assert_eq!(dirs[2].name, Some("crate2".to_owned())); assert_eq!(dirs[2].level, LevelFilter::Debug); assert!(filter.is_some() && filter.unwrap().to_string() == "abc"); } @@ -245,7 +248,7 @@ mod tests { fn parse_spec_invalid_crate_filter() { let (dirs, filter) = parse_spec("crate1::mod1=error=warn,crate2=debug/a.c"); assert_eq!(dirs.len(), 1); - assert_eq!(dirs[0].name, Some("crate2".to_string())); + assert_eq!(dirs[0].name, Some("crate2".to_owned())); assert_eq!(dirs[0].level, LevelFilter::Debug); assert!(filter.is_some() && filter.unwrap().to_string() == "a.c"); } @@ -254,7 +257,7 @@ mod tests { fn parse_spec_empty_with_filter() { let (dirs, filter) = parse_spec("crate1/a*c"); assert_eq!(dirs.len(), 1); - assert_eq!(dirs[0].name, Some("crate1".to_string())); + assert_eq!(dirs[0].name, Some("crate1".to_owned())); assert_eq!(dirs[0].level, LevelFilter::max()); assert!(filter.is_some() && filter.unwrap().to_string() == "a*c"); } diff --git a/src/fmt/humantime.rs b/src/fmt/humantime.rs index 9c93d3bf..2e77bff4 100644 --- a/src/fmt/humantime.rs +++ b/src/fmt/humantime.rs @@ -81,12 +81,12 @@ pub struct Timestamp { } impl fmt::Debug for Timestamp { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { /// A `Debug` wrapper for `Timestamp` that uses the `Display` implementation. struct TimestampValue<'a>(&'a Timestamp); impl<'a> fmt::Debug for TimestampValue<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.0, f) } } @@ -98,7 +98,7 @@ impl fmt::Debug for Timestamp { } impl fmt::Display for Timestamp { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let formatter = match self.precision { TimestampPrecision::Seconds => format_rfc3339_seconds, TimestampPrecision::Millis => format_rfc3339_millis, diff --git a/src/fmt/kv.rs b/src/fmt/kv.rs index 5d8cfcaf..55f3beaf 100644 --- a/src/fmt/kv.rs +++ b/src/fmt/kv.rs @@ -42,7 +42,7 @@ pub fn default_kv_format(formatter: &mut Formatter, fields: &dyn Source) -> io:: struct DefaultVisitSource<'a>(&'a mut Formatter); impl<'a, 'kvs> VisitSource<'kvs> for DefaultVisitSource<'a> { - fn visit_pair(&mut self, key: Key, value: Value<'kvs>) -> Result<(), Error> { + fn visit_pair(&mut self, key: Key<'_>, value: Value<'kvs>) -> Result<(), Error> { write!(self.0, " {}={}", self.style_key(key), value)?; Ok(()) } diff --git a/src/fmt/mod.rs b/src/fmt/mod.rs index f18940ad..c65f60de 100644 --- a/src/fmt/mod.rs +++ b/src/fmt/mod.rs @@ -57,7 +57,7 @@ use std::cell::RefCell; use std::fmt::Display; -use std::io::prelude::*; +use std::io::prelude::Write; use std::rc::Rc; use std::{fmt, io, mem}; @@ -88,6 +88,7 @@ use self::writer::{Buffer, Writer}; /// Seconds give precision of full seconds, milliseconds give thousands of a /// second (3 decimal digits), microseconds are millionth of a second (6 decimal /// digits) and nanoseconds are billionth of a second (9 decimal digits). +#[allow(clippy::exhaustive_enums)] // compatibility #[derive(Copy, Clone, Debug)] pub enum TimestampPrecision { /// Full second precision (0 decimal digits) @@ -149,7 +150,7 @@ impl Formatter { } pub(crate) fn clear(&mut self) { - self.buf.borrow_mut().clear() + self.buf.borrow_mut().clear(); } } @@ -188,7 +189,7 @@ impl Write for Formatter { } impl fmt::Debug for Formatter { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let buf = self.buf.borrow(); f.debug_struct("Formatter") .field("buf", &buf) @@ -197,18 +198,18 @@ impl fmt::Debug for Formatter { } } -pub(crate) type FormatFn = Box io::Result<()> + Sync + Send>; +pub(crate) type FormatFn = Box) -> io::Result<()> + Sync + Send>; pub(crate) struct Builder { - pub format_timestamp: Option, - pub format_module_path: bool, - pub format_target: bool, - pub format_level: bool, - pub format_indent: Option, - pub custom_format: Option, - pub format_suffix: &'static str, + pub(crate) format_timestamp: Option, + pub(crate) format_module_path: bool, + pub(crate) format_target: bool, + pub(crate) format_level: bool, + pub(crate) format_indent: Option, + pub(crate) custom_format: Option, + pub(crate) format_suffix: &'static str, #[cfg(feature = "unstable-kv")] - pub kv_format: Option>, + pub(crate) kv_format: Option>, built: bool, } @@ -218,7 +219,7 @@ impl Builder { /// If the `custom_format` is `Some`, then any `default_format` switches are ignored. /// If the `custom_format` is `None`, then a default format is returned. /// Any `default_format` switches set to `false` won't be written by the format. - pub fn build(&mut self) -> FormatFn { + pub(crate) fn build(&mut self) -> FormatFn { assert!(!self.built, "attempt to re-use consumed builder"); let built = mem::replace( @@ -282,7 +283,7 @@ struct StyledValue { } #[cfg(feature = "color")] -impl std::fmt::Display for StyledValue { +impl Display for StyledValue { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let style = self.style; @@ -315,7 +316,7 @@ struct DefaultFormat<'a> { } impl<'a> DefaultFormat<'a> { - fn write(mut self, record: &Record) -> io::Result<()> { + fn write(mut self, record: &Record<'_>) -> io::Result<()> { self.write_timestamp()?; self.write_level(record)?; self.write_module_path(record)?; @@ -360,7 +361,7 @@ impl<'a> DefaultFormat<'a> { } } - fn write_level(&mut self, record: &Record) -> io::Result<()> { + fn write_level(&mut self, record: &Record<'_>) -> io::Result<()> { if !self.level { return Ok(()); } @@ -386,7 +387,7 @@ impl<'a> DefaultFormat<'a> { fn write_timestamp(&mut self) -> io::Result<()> { #[cfg(feature = "humantime")] { - use self::TimestampPrecision::*; + use self::TimestampPrecision::{Micros, Millis, Nanos, Seconds}; let ts = match self.timestamp { None => return Ok(()), Some(Seconds) => self.buf.timestamp_seconds(), @@ -406,7 +407,7 @@ impl<'a> DefaultFormat<'a> { } } - fn write_module_path(&mut self, record: &Record) -> io::Result<()> { + fn write_module_path(&mut self, record: &Record<'_>) -> io::Result<()> { if !self.module_path { return Ok(()); } @@ -418,7 +419,7 @@ impl<'a> DefaultFormat<'a> { } } - fn write_target(&mut self, record: &Record) -> io::Result<()> { + fn write_target(&mut self, record: &Record<'_>) -> io::Result<()> { if !self.target { return Ok(()); } @@ -438,7 +439,7 @@ impl<'a> DefaultFormat<'a> { } } - fn write_args(&mut self, record: &Record) -> io::Result<()> { + fn write_args(&mut self, record: &Record<'_>) -> io::Result<()> { match self.indent { // Fast path for no indentation None => write!(self.buf, "{}", record.args()), @@ -446,7 +447,7 @@ impl<'a> DefaultFormat<'a> { Some(indent_count) => { // Create a wrapper around the buffer only if we have to actually indent the message - struct IndentWrapper<'a, 'b: 'a> { + struct IndentWrapper<'a, 'b> { fmt: &'a mut DefaultFormat<'b>, indent_count: usize, } @@ -491,7 +492,7 @@ impl<'a> DefaultFormat<'a> { } #[cfg(feature = "unstable-kv")] - fn write_kv(&mut self, record: &Record) -> io::Result<()> { + fn write_kv(&mut self, record: &Record<'_>) -> io::Result<()> { let format = self.kv_format; format(self.buf, record.key_values()) } @@ -503,7 +504,7 @@ mod tests { use log::{Level, Record}; - fn write_record(record: Record, fmt: DefaultFormat) -> String { + fn write_record(record: Record<'_>, fmt: DefaultFormat<'_>) -> String { let buf = fmt.buf.buf.clone(); fmt.write(&record).expect("failed to write record"); @@ -512,7 +513,7 @@ mod tests { String::from_utf8(buf.as_bytes().to_vec()).expect("failed to read record") } - fn write_target(target: &str, fmt: DefaultFormat) -> String { + fn write_target(target: &str, fmt: DefaultFormat<'_>) -> String { write_record( Record::builder() .args(format_args!("log\nmessage")) @@ -526,7 +527,7 @@ mod tests { ) } - fn write(fmt: DefaultFormat) -> String { + fn write(fmt: DefaultFormat<'_>) -> String { write_target("", fmt) } diff --git a/src/fmt/writer/buffer.rs b/src/fmt/writer/buffer.rs index b720a988..d4d8bb88 100644 --- a/src/fmt/writer/buffer.rs +++ b/src/fmt/writer/buffer.rs @@ -50,6 +50,9 @@ impl BufferWriter { } pub(in crate::fmt::writer) fn print(&self, buf: &Buffer) -> io::Result<()> { + #![allow(clippy::print_stdout)] // enabled for tests only + #![allow(clippy::print_stderr)] // enabled for tests only + use std::io::Write as _; let buf = buf.as_bytes(); @@ -91,7 +94,7 @@ impl BufferWriter { let buf = adapt(buf, self.write_style)?; #[cfg(feature = "color")] let buf = &buf; - let mut stream = pipe.lock().unwrap(); + let mut stream = pipe.lock().expect("no panics while held"); stream.write_all(buf)?; stream.flush()?; } @@ -134,7 +137,7 @@ impl Buffer { } impl std::fmt::Debug for Buffer { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { String::from_utf8_lossy(self.as_bytes()).fmt(f) } } @@ -152,7 +155,7 @@ pub(super) enum WritableTarget { /// Logs will be printed to standard error. PrintStderr, /// Logs will be sent to a custom pipe. - Pipe(Box>), + Pipe(Box>), } impl std::fmt::Debug for WritableTarget { diff --git a/src/fmt/writer/mod.rs b/src/fmt/writer/mod.rs index 688fda16..2131d746 100644 --- a/src/fmt/writer/mod.rs +++ b/src/fmt/writer/mod.rs @@ -9,6 +9,7 @@ pub(super) use self::buffer::Buffer; pub use target::Target; /// Whether or not to print styles to the target. +#[allow(clippy::exhaustive_enums)] // By definition don't need more #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Default)] pub enum WriteStyle { /// Try to print styles, but don't force the issue. @@ -50,7 +51,7 @@ pub(crate) struct Writer { } impl Writer { - pub fn write_style(&self) -> WriteStyle { + pub(crate) fn write_style(&self) -> WriteStyle { self.inner.write_style() } diff --git a/src/lib.rs b/src/lib.rs index 86db145b..18a7a027 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -260,12 +260,10 @@ //! [level-enum]: https://docs.rs/log/latest/log/enum.Level.html //! [log-crate-url]: https://docs.rs/log -#![doc( - html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", - html_favicon_url = "https://www.rust-lang.org/static/images/favicon.ico" -)] #![cfg_attr(docsrs, feature(doc_auto_cfg))] -#![deny(missing_debug_implementations, missing_docs)] +#![warn(missing_docs)] +#![warn(clippy::print_stderr)] +#![warn(clippy::print_stdout)] mod logger; diff --git a/src/logger.rs b/src/logger.rs index 6f414904..1639c079 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -242,7 +242,7 @@ impl Builder { /// [`std::fmt`]: https://doc.rust-lang.org/std/fmt/index.html pub fn format(&mut self, format: F) -> &mut Self where - F: Fn(&mut Formatter, &Record) -> io::Result<()> + Sync + Send, + F: Fn(&mut Formatter, &Record<'_>) -> io::Result<()> + Sync + Send, { self.format.custom_format = Some(Box::new(format)); self @@ -516,7 +516,7 @@ impl Builder { } impl std::fmt::Debug for Builder { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if self.built { f.debug_struct("Logger").field("built", &true).finish() } else { @@ -615,17 +615,17 @@ impl Logger { } /// Checks if this record matches the configured filter. - pub fn matches(&self, record: &Record) -> bool { + pub fn matches(&self, record: &Record<'_>) -> bool { self.filter.matches(record) } } impl Log for Logger { - fn enabled(&self, metadata: &Metadata) -> bool { + fn enabled(&self, metadata: &Metadata<'_>) -> bool { self.filter.enabled(metadata) } - fn log(&self, record: &Record) { + fn log(&self, record: &Record<'_>) { if self.matches(record) { // Log records are written to a thread-local buffer before being printed // to the terminal. We clear these buffers afterwards, but they aren't shrunk @@ -637,10 +637,10 @@ impl Log for Logger { // formatter and its buffer are discarded and recreated. thread_local! { - static FORMATTER: RefCell> = RefCell::new(None); + static FORMATTER: RefCell> = const { RefCell::new(None) }; } - let print = |formatter: &mut Formatter, record: &Record| { + let print = |formatter: &mut Formatter, record: &Record<'_>| { let _ = (self.format)(formatter, record).and_then(|_| formatter.print(&self.writer)); @@ -650,31 +650,28 @@ impl Log for Logger { let printed = FORMATTER .try_with(|tl_buf| { - match tl_buf.try_borrow_mut() { + if let Ok(mut tl_buf) = tl_buf.try_borrow_mut() { // There are no active borrows of the buffer - Ok(mut tl_buf) => match *tl_buf { + if let Some(ref mut formatter) = *tl_buf { // We have a previously set formatter - Some(ref mut formatter) => { - // Check the buffer style. If it's different from the logger's - // style then drop the buffer and recreate it. - if formatter.write_style() != self.writer.write_style() { - *formatter = Formatter::new(&self.writer); - } - - print(formatter, record); + + // Check the buffer style. If it's different from the logger's + // style then drop the buffer and recreate it. + if formatter.write_style() != self.writer.write_style() { + *formatter = Formatter::new(&self.writer); } + + print(formatter, record); + } else { // We don't have a previously set formatter - None => { - let mut formatter = Formatter::new(&self.writer); - print(&mut formatter, record); + let mut formatter = Formatter::new(&self.writer); + print(&mut formatter, record); - *tl_buf = Some(formatter); - } - }, - // There's already an active borrow of the buffer (due to re-entrancy) - Err(_) => { - print(&mut Formatter::new(&self.writer), record); + *tl_buf = Some(formatter); } + } else { + // There's already an active borrow of the buffer (due to re-entrancy) + print(&mut Formatter::new(&self.writer), record); } }) .is_ok(); @@ -692,7 +689,7 @@ impl Log for Logger { } impl std::fmt::Debug for Logger { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Logger") .field("filter", &self.filter) .finish() @@ -849,7 +846,7 @@ impl<'a> Var<'a> { fn get(&self) -> Option { env::var(&*self.name) .ok() - .or_else(|| self.default.to_owned().map(|v| v.into_owned())) + .or_else(|| self.default.clone().map(|v| v.into_owned())) } } diff --git a/tests/init-twice-retains-filter.rs b/tests/init-twice-retains-filter.rs index 673da3fd..cc9fded0 100644 --- a/tests/init-twice-retains-filter.rs +++ b/tests/init-twice-retains-filter.rs @@ -1,5 +1,4 @@ -extern crate env_logger; -extern crate log; +#![allow(clippy::unwrap_used)] use std::env; use std::process; diff --git a/tests/log-in-log.rs b/tests/log-in-log.rs index 89517ff3..1ed9be96 100644 --- a/tests/log-in-log.rs +++ b/tests/log-in-log.rs @@ -1,6 +1,7 @@ +#![allow(clippy::unwrap_used)] + #[macro_use] extern crate log; -extern crate env_logger; use std::env; use std::fmt; @@ -10,7 +11,7 @@ use std::str; struct Foo; impl fmt::Display for Foo { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { info!("test"); f.write_str("bar") } diff --git a/tests/log_tls_dtors.rs b/tests/log_tls_dtors.rs index 5db87bd6..8ebae39f 100644 --- a/tests/log_tls_dtors.rs +++ b/tests/log_tls_dtors.rs @@ -1,6 +1,7 @@ +#![allow(clippy::unwrap_used)] + #[macro_use] extern crate log; -extern crate env_logger; use std::env; use std::process; @@ -19,16 +20,16 @@ fn run() { // Use multiple thread local values to increase the chance that our TLS // value will get destroyed after the FORMATTER key in the library thread_local! { - static DROP_ME_0: DropMe = DropMe; - static DROP_ME_1: DropMe = DropMe; - static DROP_ME_2: DropMe = DropMe; - static DROP_ME_3: DropMe = DropMe; - static DROP_ME_4: DropMe = DropMe; - static DROP_ME_5: DropMe = DropMe; - static DROP_ME_6: DropMe = DropMe; - static DROP_ME_7: DropMe = DropMe; - static DROP_ME_8: DropMe = DropMe; - static DROP_ME_9: DropMe = DropMe; + static DROP_ME_0: DropMe = const { DropMe }; + static DROP_ME_1: DropMe = const { DropMe }; + static DROP_ME_2: DropMe = const { DropMe }; + static DROP_ME_3: DropMe = const { DropMe }; + static DROP_ME_4: DropMe = const { DropMe }; + static DROP_ME_5: DropMe = const { DropMe }; + static DROP_ME_6: DropMe = const { DropMe }; + static DROP_ME_7: DropMe = const { DropMe }; + static DROP_ME_8: DropMe = const { DropMe }; + static DROP_ME_9: DropMe = const { DropMe }; } DROP_ME_0.with(|_| {}); DROP_ME_1.with(|_| {}); diff --git a/tests/regexp_filter.rs b/tests/regexp_filter.rs index 40178bac..af2864db 100644 --- a/tests/regexp_filter.rs +++ b/tests/regexp_filter.rs @@ -1,6 +1,7 @@ +#![allow(clippy::unwrap_used)] + #[macro_use] extern crate log; -extern crate env_logger; use std::env; use std::process; @@ -10,7 +11,7 @@ fn main() { if env::var("LOG_REGEXP_TEST").ok() == Some(String::from("1")) { child_main(); } else { - parent_main() + parent_main(); } } @@ -32,13 +33,13 @@ fn run_child(rust_log: String) -> bool { } fn assert_message_printed(rust_log: &str) { - if !run_child(rust_log.to_string()) { + if !run_child(rust_log.to_owned()) { panic!("RUST_LOG={} should allow the test log message", rust_log) } } fn assert_message_not_printed(rust_log: &str) { - if run_child(rust_log.to_string()) { + if run_child(rust_log.to_owned()) { panic!( "RUST_LOG={} should not allow the test log message", rust_log