From fbd08ec24dbd036c89f5f0af4d936b616790a166 Mon Sep 17 00:00:00 2001 From: tyranron Date: Fri, 8 Apr 2022 13:17:56 +0300 Subject: [PATCH] Tune up Cargo features according to 1.60 Rust and bump up MSRV Additionally: - tune lints for 1.60 Rust --- .github/workflows/ci.yml | 2 +- CHANGELOG.md | 13 ++++++++ Cargo.toml | 12 ++++---- README.md | 3 +- book/src/quickstart.md | 2 +- book/tests/Cargo.toml | 2 +- codegen/CHANGELOG.md | 13 ++++++++ codegen/Cargo.toml | 4 +-- codegen/README.md | 3 +- codegen/src/attribute.rs | 20 ++++--------- codegen/src/lib.rs | 3 ++ codegen/src/parameter.rs | 3 +- codegen/src/world_init.rs | 3 +- src/cucumber.rs | 13 ++------ src/event.rs | 3 +- src/feature.rs | 3 ++ src/lib.rs | 3 ++ src/parser/basic.rs | 4 +-- src/runner/basic.rs | 9 ++---- src/step.rs | 9 ++---- src/writer/basic.rs | 62 ++++++++++----------------------------- src/writer/discard.rs | 1 - src/writer/json.rs | 27 +++++++---------- src/writer/junit.rs | 58 +++++++++++++----------------------- src/writer/normalize.rs | 6 ++-- src/writer/out.rs | 6 ++-- src/writer/summarize.rs | 25 ++++------------ tests/output.rs | 19 ++++-------- 28 files changed, 131 insertions(+), 200 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 143e6599..17b300b9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -100,7 +100,7 @@ jobs: strategy: fail-fast: false matrix: - msrv: ['1.57.0'] + msrv: ["1.60.0"] crate: - cucumber-codegen - cucumber diff --git a/CHANGELOG.md b/CHANGELOG.md index de587e40..8ce4bd3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,18 @@ All user visible changes to `cucumber` crate will be documented in this file. Th +## [0.14.0] · 2022-??-?? (unreleased) +[0.14.0]: /../../tree/v0.14.0 + +[Diff](/../../compare/v0.13.0...v0.14.0) | [Milestone](/../../milestone/13) + +### BC Breaks + +- Bump up [MSRV] to 1.60 for more clever support of [Cargo feature]s. + + + + ## [0.13.0] · 2022-03-29 [0.13.0]: /../../tree/v0.13.0 @@ -416,6 +428,7 @@ All user visible changes to `cucumber` crate will be documented in this file. Th [`gherkin`]: https://docs.rs/gherkin [`gherkin_rust`]: https://docs.rs/gherkin_rust +[Cargo feature]: https://doc.rust-lang.org/cargo/reference/features.html [Cucumber Expressions]: https://cucumber.github.io/cucumber-expressions [MSRV]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-rust-version-field [Semantic Versioning 2.0.0]: https://semver.org diff --git a/Cargo.toml b/Cargo.toml index bc66126e..6e03092f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "cucumber" -version = "0.13.0" +version = "0.14.0-dev" edition = "2021" -rust-version = "1.57" +rust-version = "1.60" description = """\ Cucumber testing framework for Rust, with async support. \ Fully native, no external test runners or dependencies.\ @@ -28,11 +28,11 @@ rustdoc-args = ["--cfg", "docsrs"] [features] default = ["macros"] # Enables step attributes and auto-wiring. -macros = ["cucumber-codegen", "cucumber-expressions", "inventory"] +macros = ["dep:cucumber-codegen", "dep:cucumber-expressions", "dep:inventory"] # Enables support for outputting in Cucumber JSON format. -output-json = ["Inflector", "serde", "serde_json", "timestamps"] +output-json = ["dep:Inflector", "dep:serde", "dep:serde_json", "timestamps"] # Enables support for outputting JUnit XML report. -output-junit = ["junit-report", "timestamps"] +output-junit = ["dep:junit-report", "timestamps"] # Enables timestamps collecting for all events. timestamps = [] @@ -53,7 +53,7 @@ regex = "1.5.5" sealed = "0.4" # "macros" feature dependencies. -cucumber-codegen = { version = "0.13", path = "./codegen", optional = true } +cucumber-codegen = { version = "0.14.0-dev", path = "./codegen", optional = true } cucumber-expressions = { version = "0.2.1", features = ["into-regex"], optional = true } inventory = { version = "0.2", optional = true } diff --git a/README.md b/README.md index 43790345..d56f4c6c 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,10 @@ Cucumber testing framework for Rust =================================== +[![Crates.io](https://img.shields.io/crates/v/cucumber.svg?maxAge=2592000)](https://crates.io/crates/cucumber) [![Documentation](https://docs.rs/cucumber/badge.svg)](https://docs.rs/cucumber) [![CI](https://github.com/cucumber-rs/cucumber/workflows/CI/badge.svg?branch=main "CI")](https://github.com/cucumber-rs/cucumber/actions?query=workflow%3ACI+branch%3Amain) -[![Rust 1.57+](https://img.shields.io/badge/rustc-1.57+-lightgray.svg "Rust 1.57+")](https://blog.rust-lang.org/2021/12/02/Rust-1.57.0.html) +[![Rust 1.60+](https://img.shields.io/badge/rustc-1.60+-lightgray.svg "Rust 1.60+")](https://blog.rust-lang.org/2022/04/07/Rust-1.60.0.html) [![Unsafe Forbidden](https://img.shields.io/badge/unsafe-forbidden-success.svg)](https://github.com/rust-secure-code/safety-dance) An implementation of the [Cucumber] testing framework for Rust. Fully native, no external test runners or dependencies. diff --git a/book/src/quickstart.md b/book/src/quickstart.md index 0b8aa839..4ff1a7de 100644 --- a/book/src/quickstart.md +++ b/book/src/quickstart.md @@ -395,7 +395,7 @@ fn hungry_cat(world: &mut AnimalWorld, state: String) { match state.as_str() { "hungry" => world.cat.hungry = true, "satiated" => world.cat.hungry = false, - s => panic!("expected 'hungry' or 'satiated', found: {}", s), + s => panic!("expected 'hungry' or 'satiated', found: {s}"), } } # diff --git a/book/tests/Cargo.toml b/book/tests/Cargo.toml index fd59e9f7..6535f404 100644 --- a/book/tests/Cargo.toml +++ b/book/tests/Cargo.toml @@ -12,7 +12,7 @@ publish = false [dependencies] async-trait = "0.1" clap = { version = "3.0", features = ["derive"] } -cucumber = { version = "0.13", path = "../..", features = ["output-json", "output-junit"] } +cucumber = { version = "0.14.0-dev", path = "../..", features = ["output-json", "output-junit"] } futures = "0.3" humantime = "2.1" once_cell = { version = "1.8", features = ["parking_lot"] } diff --git a/codegen/CHANGELOG.md b/codegen/CHANGELOG.md index 210e2e42..e9d1e1ef 100644 --- a/codegen/CHANGELOG.md +++ b/codegen/CHANGELOG.md @@ -6,6 +6,18 @@ All user visible changes to `cucumber-codegen` crate will be documented in this +## [0.14.0] · 2022-??-?? (unreleased) +[0.14.0]: /../../tree/v0.14.0/codegen + +[Milestone](/../../milestone/13) + +### BC Breaks + +- Bump up [MSRV] to 1.60 for more clever support of [Cargo feature]s. + + + + ## [0.13.0] · 2022-03-29 [0.13.0]: /../../tree/v0.13.0/codegen @@ -173,6 +185,7 @@ See `cucumber` crate [changelog](https://github.com/cucumber-rs/cucumber/blob/v0 +[Cargo feature]: https://doc.rust-lang.org/cargo/reference/features.html [Cucumber Expressions]: https://cucumber.github.io/cucumber-expressions [MSRV]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-rust-version-field [Semantic Versioning 2.0.0]: https://semver.org diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml index 5820ec06..96009469 100644 --- a/codegen/Cargo.toml +++ b/codegen/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "cucumber-codegen" -version = "0.13.0" # should be the same as main crate version +version = "0.14.0-dev" # should be the same as main crate version edition = "2021" -rust-version = "1.57" +rust-version = "1.60" description = "Code generation for `cucumber` crate." license = "MIT OR Apache-2.0" authors = [ diff --git a/codegen/README.md b/codegen/README.md index 5913934f..ed118488 100644 --- a/codegen/README.md +++ b/codegen/README.md @@ -1,9 +1,10 @@ `cucumber-codegen` crate ======================== +[![Crates.io](https://img.shields.io/crates/v/cucumber-codegen.svg?maxAge=2592000)](https://crates.io/crates/cucumber-codegen) [![Documentation](https://docs.rs/cucumber-codegen/badge.svg)](https://docs.rs/cucumber-codegen) [![CI](https://github.com/cucumber-rs/cucumber/workflows/CI/badge.svg?branch=main "CI")](https://github.com/cucumber-rs/cucumber/actions?query=workflow%3ACI+branch%3Amain) -[![Rust 1.57+](https://img.shields.io/badge/rustc-1.57+-lightgray.svg "Rust 1.57+")](https://blog.rust-lang.org/2021/12/02/Rust-1.57.0.html) +[![Rust 1.60+](https://img.shields.io/badge/rustc-1.60+-lightgray.svg "Rust 1.60+")](https://blog.rust-lang.org/2022/04/07/Rust-1.60.0.html) [![Unsafe Forbidden](https://img.shields.io/badge/unsafe-forbidden-success.svg)](https://github.com/rust-secure-code/safety-dance) - [Changelog](https://github.com/cucumber-rs/cucumber/blob/main/codegen/CHANGELOG.md) diff --git a/codegen/src/attribute.rs b/codegen/src/attribute.rs index ecfc2641..d39541cf 100644 --- a/codegen/src/attribute.rs +++ b/codegen/src/attribute.rs @@ -113,11 +113,9 @@ impl Step { let regex = self.gen_regex()?; - // TODO: Use "{func_name}" syntax once MSRV bumps above 1.58. let caller_name = - format_ident!("__cucumber_{}_{}", self.attr_name, func_name); + format_ident!("__cucumber_{}_{func_name}", self.attr_name); let awaiting = func.sig.asyncness.map(|_| quote! { .await }); - // TODO: Use "{e}" syntax once MSRV bumps above 1.58. let unwrapping = (!self.returns_unit()) .then(|| quote! { .unwrap_or_else(|e| panic!("{}", e)) }); let step_caller = quote! { @@ -348,11 +346,9 @@ impl Step { return Err(syn::Error::new(ty.span(), "Type path expected")); }; - // TODO: Use "{ident}" syntax once MSRV bumps above 1.58. - let not_found_err = format!("{} not found", ident); + let not_found_err = format!("{ident} not found"); let parsing_err = format!( - "{} can not be parsed to {}", - ident, + "{ident} can not be parsed to {}", ty.path .segments .last() @@ -456,8 +452,7 @@ impl Step { } AttributeArgument::Regex(re) => { drop(Regex::new(re.value().as_str()).map_err(|e| { - // TODO: Use "{e}" syntax once MSRV bumps above 1.58. - syn::Error::new(re.span(), format!("Invalid regex: {}", e)) + syn::Error::new(re.span(), format!("Invalid regex: {e}")) })?); Ok(quote! { ::cucumber::codegen::Regex::new(#re).unwrap() }) @@ -541,8 +536,7 @@ impl<'p> Parameters<'p> { let expr = Expression::parse(expr).map_err(|e| { syn::Error::new( expr.span(), - // TODO: Use "{e}" syntax once MSRV bumps above 1.58. - format!("Incorrect cucumber expression: {}", e), + format!("Incorrect cucumber expression: {e}"), ) })?; @@ -667,12 +661,10 @@ impl<'p> Parameters<'p> { } else { // Here we use double escaping to properly render `{name}` // in the assertion message of the generated code. - // TODO: Use "{name}" syntax once MSRV bumps above 1.58. let assert_msg = format!( "Type `{}` doesn't implement a custom parameter \ - `{{{{{}}}}}`", + `{{{{{name}}}}}`", quote! { #ty }, - name, ); quote! { diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index b8728685..a045abe7 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -30,6 +30,7 @@ clippy::dbg_macro, clippy::debug_assert_with_mut_call, clippy::decimal_literal_representation, + clippy::default_union_representation, clippy::else_if_without_else, clippy::empty_line_after_outer_attr, clippy::equatable_if_let, @@ -50,6 +51,7 @@ clippy::missing_const_for_fn, clippy::missing_docs_in_private_items, clippy::multiple_inherent_impl, + clippy::mutex_atomic, clippy::mutex_integer, clippy::nonstandard_macro_braces, clippy::option_if_let_else, @@ -71,6 +73,7 @@ clippy::suspicious_operation_groupings, clippy::todo, clippy::trailing_empty_array, + clippy::transmute_undefined_repr, clippy::trivial_regex, clippy::undocumented_unsafe_blocks, clippy::unimplemented, diff --git a/codegen/src/parameter.rs b/codegen/src/parameter.rs index 0f774c80..9df6953a 100644 --- a/codegen/src/parameter.rs +++ b/codegen/src/parameter.rs @@ -65,8 +65,7 @@ impl TryFrom for Definition { let attrs: Attrs = Attrs::parse_attrs("param", &input)?; let regex = Regex::new(&attrs.regex.value()).map_err(|e| { - // TODO: Use "{e}" syntax once MSRV bumps above 1.58. - syn::Error::new(attrs.regex.span(), format!("Invalid regex: {}", e)) + syn::Error::new(attrs.regex.span(), format!("Invalid regex: {e}")) })?; let name = attrs.name.as_ref().map_or_else( diff --git a/codegen/src/world_init.rs b/codegen/src/world_init.rs index fb4ce98e..5c51c15d 100644 --- a/codegen/src/world_init.rs +++ b/codegen/src/world_init.rs @@ -46,10 +46,9 @@ pub(crate) fn derive( /// /// [`syn::Ident`]: struct@syn::Ident fn step_types(steps: &[&str], world: &syn::Ident) -> Vec { - // TODO: Use "{world}" syntax once MSRV bumps above 1.58. steps .iter() - .map(|step| format_ident!("Cucumber{}{}", to_pascal_case(step), world)) + .map(|step| format_ident!("Cucumber{}{world}", to_pascal_case(step))) .collect() } diff --git a/src/cucumber.rs b/src/cucumber.rs index ead6b983..c11322f4 100644 --- a/src/cucumber.rs +++ b/src/cucumber.rs @@ -1272,31 +1272,24 @@ where let failed_steps = writer.failed_steps(); if failed_steps > 0 { - // TODO: Use "{failed_steps}" syntax once MSRV bumps above 1.58. msg.push(format!( - "{} step{} failed", - failed_steps, + "{failed_steps} step{} failed", (failed_steps > 1).then(|| "s").unwrap_or_default(), )); } let parsing_errors = writer.parsing_errors(); if parsing_errors > 0 { - // TODO: Use "{parsing_errors}" syntax once MSRV bumps above - // 1.58. msg.push(format!( - "{} parsing error{}", - parsing_errors, + "{parsing_errors} parsing error{}", (parsing_errors > 1).then(|| "s").unwrap_or_default(), )); } let hook_errors = writer.hook_errors(); if hook_errors > 0 { - // TODO: Use "{hook_errors}" syntax once MSRV bumps above 1.58. msg.push(format!( - "{} hook error{}", - hook_errors, + "{hook_errors} hook error{}", (hook_errors > 1).then(|| "s").unwrap_or_default(), )); } diff --git a/src/event.rs b/src/event.rs index 624bfb42..41960e03 100644 --- a/src/event.rs +++ b/src/event.rs @@ -356,8 +356,7 @@ pub enum HookType { impl fmt::Display for HookType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // TODO: Use "{self}" syntax once MSRV bumps above 1.58. - write!(f, "{:?}", self) + write!(f, "{self:?}") } } diff --git a/src/feature.rs b/src/feature.rs index f4a3578b..42442ee4 100644 --- a/src/feature.rs +++ b/src/feature.rs @@ -153,6 +153,9 @@ fn expand_scenario( let mut err = None; let replaced = TEMPLATE_REGEX .replace_all(str, |cap: ®ex::Captures<'_>| { + // PANIC: Unwrapping is OK here as `TEMPLATE_REGEX` + // contains this capture group. + #[allow(clippy::unwrap_used)] let name = cap.get(1).unwrap().as_str(); row.clone() diff --git a/src/lib.rs b/src/lib.rs index 2c87bfd5..eed34cfa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,6 +31,7 @@ clippy::dbg_macro, clippy::debug_assert_with_mut_call, clippy::decimal_literal_representation, + clippy::default_union_representation, clippy::else_if_without_else, clippy::empty_line_after_outer_attr, clippy::equatable_if_let, @@ -52,6 +53,7 @@ clippy::missing_const_for_fn, clippy::missing_docs_in_private_items, clippy::multiple_inherent_impl, + clippy::mutex_atomic, clippy::mutex_integer, clippy::nonstandard_macro_braces, clippy::option_if_let_else, @@ -73,6 +75,7 @@ clippy::suspicious_operation_groupings, clippy::todo, clippy::trailing_empty_array, + clippy::transmute_undefined_repr, clippy::trivial_regex, clippy::undocumented_unsafe_blocks, clippy::unimplemented, diff --git a/src/parser/basic.rs b/src/parser/basic.rs index e34e7420..14887c41 100644 --- a/src/parser/basic.rs +++ b/src/parser/basic.rs @@ -110,9 +110,7 @@ impl> Parser for Basic { .case_insensitive(true) .build() .unwrap_or_else(|e| { - // TODO: Use "{e}" syntax once MSRV bumps above - // 1.58. - unreachable!("GlobWalkerBuilder panicked: {}", e) + unreachable!("GlobWalkerBuilder panicked: {e}") }); walk(w) } diff --git a/src/runner/basic.rs b/src/runner/basic.rs index 6283e7b6..2138391e 100644 --- a/src/runner/basic.rs +++ b/src/runner/basic.rs @@ -866,11 +866,8 @@ where .map_err(Info::from) .and_then(|r| { r.map_err(|e| { - // TODO: Use "{step:p}" syntax once MSRV bumps above - // 1.58. coerce_into_info(format!( - "failed to initialize World: {}", - e, + "failed to initialize World: {e}", )) }) }) @@ -961,9 +958,7 @@ where Ok(Ok(w)) => w, Ok(Err(e)) => { let e = event::StepError::Panic(coerce_into_info( - // TODO: Use "{step:p}" syntax once MSRV bumps above - // 1.58. - format!("failed to initialize World: {}", e), + format!("failed to initialize World: {e}"), )); return Err((e, None, None)); } diff --git a/src/step.rs b/src/step.rs index 149ba1b3..b1d91377 100644 --- a/src/step.rs +++ b/src/step.rs @@ -60,29 +60,26 @@ impl fmt::Debug for Collection { f.debug_struct("Collection") .field( "given", - // TODO: Use "{step:p}" syntax once MSRV bumps above 1.58. &self .given .iter() - .map(|(re, step)| (re, format!("{:p}", step))) + .map(|(re, step)| (re, format!("{step:p}"))) .collect::>(), ) .field( "when", - // TODO: Use "{step:p}" syntax once MSRV bumps above 1.58. &self .when .iter() - .map(|(re, step)| (re, format!("{:p}", step))) + .map(|(re, step)| (re, format!("{step:p}"))) .collect::>(), ) .field( "then", - // TODO: Use "{step:p}" syntax once MSRV bumps above 1.58. &self .then .iter() - .map(|(re, step)| (re, format!("{:p}", step))) + .map(|(re, step)| (re, format!("{step:p}"))) .collect::>(), ) .finish() diff --git a/src/writer/basic.rs b/src/writer/basic.rs index fac7485b..da8f6f1e 100644 --- a/src/writer/basic.rs +++ b/src/writer/basic.rs @@ -126,7 +126,6 @@ where { type Cli = Cli; - #[allow(clippy::unused_async)] // false positive: #[async_trait] async fn handle_event( &mut self, ev: parser::Result>>, @@ -146,10 +145,7 @@ where Feature::Finished => Ok(()), }, } - .unwrap_or_else(|e| { - // TODO: Use "{e}" syntax once MSRV bumps above 1.58. - panic!("Failed to write into terminal: {}", e) - }); + .unwrap_or_else(|e| panic!("Failed to write into terminal: {e}")); } } @@ -160,14 +156,12 @@ where Val: AsRef + 'val, Out: io::Write, { - #[allow(clippy::unused_async)] // false positive: #[async_trait] async fn write(&mut self, val: Val) where 'val: 'async_trait, { - // TODO: Use "{e}" syntax once MSRV bumps above 1.58. self.write_line(val.as_ref()) - .unwrap_or_else(|e| panic!("Failed to write: {}", e)); + .unwrap_or_else(|e| panic!("Failed to write: {e}")); } } @@ -253,9 +247,8 @@ impl Basic { &mut self, error: impl Display, ) -> io::Result<()> { - // TODO: Use "{error}" syntax once MSRV bumps above 1.58. self.output - .write_line(&self.styles.err(format!("Failed to parse: {}", error))) + .write_line(&self.styles.err(format!("Failed to parse: {error}"))) } /// Outputs the [started] [`Feature`]. @@ -374,9 +367,8 @@ impl Basic { self.clear_last_lines_if_term_present()?; self.output.write_line(&self.styles.err(format!( - "{indent}✘ Scenario's {} hook failed {}:{}:{}\n\ + "{indent}✘ Scenario's {which} hook failed {}:{}:{}\n\ {indent} Captured output: {}{}", - which, feat.path .as_ref() .and_then(|p| p.to_str()) @@ -389,8 +381,7 @@ impl Basic { ), world .map(|w| format_str_with_indent( - // TODO: Use "{w:#?}" syntax once MSRV bumps above 1.58. - format!("{:#?}", w), + format!("{w:#?}"), self.indent.saturating_sub(3) + 3, )) .unwrap_or_default(), @@ -530,13 +521,8 @@ impl Basic { .map(|t| format_table(t, self.indent)) .unwrap_or_default()); - // TODO: Use "{step_keyword}" syntax once MSRV bumps above 1.58. self.output.write_line(&self.styles.ok(format!( - "{indent}{}{}{}{}", - step_keyword, - step_value, - doc_str, - step_table, + "{indent}{step_keyword}{step_value}{doc_str}{step_table}", indent = " ".repeat(self.indent.saturating_sub(3)), ))) } @@ -640,8 +626,7 @@ impl Basic { ), world .map(|w| format_str_with_indent( - // TODO: Use "{w:#?}" syntax once MSRV bumps above 1.58. - format!("{:#?}", w), + format!("{w:#?}"), self.indent.saturating_sub(3) + 3, )) .filter(|_| self.verbosity.shows_world()) @@ -649,11 +634,7 @@ impl Basic { indent = " ".repeat(self.indent.saturating_sub(3)) )); - // TODO: Use "{step_keyword}" syntax once MSRV bumps above 1.58. - self.write_line(&format!( - "{}{}{}", - step_keyword, step_value, diagnostics, - )) + self.write_line(&format!("{step_keyword}{step_value}{diagnostics}",)) } /// Outputs the [`Background`] [`Step`]'s @@ -774,13 +755,8 @@ impl Basic { .map(|t| format_table(t, self.indent)) .unwrap_or_default()); - // TODO: Use "{step_keyword}" syntax once MSRV bumps above 1.58. self.output.write_line(&self.styles.ok(format!( - "{indent}{}{}{}{}", - step_keyword, - step_value, - doc_str, - step_table, + "{indent}{step_keyword}{step_value}{doc_str}{step_table}", indent = " ".repeat(self.indent.saturating_sub(3)), ))) } @@ -886,19 +862,14 @@ impl Basic { ), world .map(|w| format_str_with_indent( - // TODO: Use "{w:#?}" syntax once MSRV bumps above 1.58. - format!("{:#?}", w), + format!("{w:#?}"), self.indent.saturating_sub(3) + 3, )) .unwrap_or_default(), indent = " ".repeat(self.indent.saturating_sub(3)) )); - // TODO: Use "{step_keyword}" syntax once MSRV bumps above 1.58. - self.write_line(&format!( - "{}{}{}", - step_keyword, step_value, diagnostics, - )) + self.write_line(&format!("{step_keyword}{step_value}{diagnostics}",)) } } @@ -916,15 +887,13 @@ pub(crate) fn coerce_error(err: &Info) -> Cow<'static, str> { /// Formats the given [`str`] by adding `indent`s to each line to prettify the /// output. fn format_str_with_indent(str: impl AsRef, indent: usize) -> String { - // TODO: Use "{line}" syntax once MSRV bumps above 1.58. let str = str .as_ref() .lines() - .map(|line| format!("{}{}", " ".repeat(indent), line)) + .map(|line| format!("{}{line}", " ".repeat(indent))) .join("\n"); - // TODO: Use "{str}" syntax once MSRV bumps above 1.58. (!str.is_empty()) - .then(|| format!("\n{}", str)) + .then(|| format!("\n{str}")) .unwrap_or_default() } @@ -948,17 +917,16 @@ fn format_table(table: &gherkin::Table, indent: usize) -> String { }) .unwrap_or_default(); - // TODO: Use "{cell:len$}" and "{row}" syntax once MSRV bumps above 1.58. let mut table = table .rows .iter() .map(|row| { row.iter() .zip(&max_row_len) - .map(|(cell, len)| format!("| {:1$} ", cell, len)) + .map(|(cell, len)| format!("| {cell:len$} ")) .collect::() }) - .map(|row| format!("{}{}", " ".repeat(indent + 1), row)) + .map(|row| format!("{}{row}", " ".repeat(indent + 1))) .join("|\n"); if !table.is_empty() { diff --git a/src/writer/discard.rs b/src/writer/discard.rs index 51610102..d6b30c66 100644 --- a/src/writer/discard.rs +++ b/src/writer/discard.rs @@ -45,7 +45,6 @@ where Wr: Writer + ?Sized, { /// Does nothing. - #[allow(clippy::unused_async)] // false positive: #[async_trait] async fn write(&mut self, _: Val) where 'val: 'async_trait, diff --git a/src/writer/json.rs b/src/writer/json.rs index 056717d7..6a4b5088 100644 --- a/src/writer/json.rs +++ b/src/writer/json.rs @@ -60,7 +60,6 @@ pub struct Json { impl Writer for Json { type Cli = cli::Empty; - #[allow(clippy::unused_async)] // false positive: #[async_trait] async fn handle_event( &mut self, event: parser::Result>>, @@ -97,16 +96,11 @@ impl Writer for Json { .write_all( serde_json::to_string(&self.features) .unwrap_or_else(|e| { - // TODO: Use "{e}" syntax once MSRV bumps above - // 1.58. - panic!("Failed to serialize JSON: {}", e) + panic!("Failed to serialize JSON: {e}") }) .as_bytes(), ) - .unwrap_or_else(|e| { - // TODO: Use "{e}" syntax once MSRV bumps above 1.58. - panic!("Failed to write JSON: {}", e) - }); + .unwrap_or_else(|e| panic!("Failed to write JSON: {e}")); } _ => {} } @@ -209,16 +203,15 @@ impl Json { let mut duration = || { let started = self.started.take().unwrap_or_else(|| { - // TODO: Use "{hook_ty}" syntax once MSRV bumps above 1.58. - panic!("No `Started` event for `{} Hook`", hook_ty) + panic!("No `Started` event for `{hook_ty} Hook`") }); meta.at .duration_since(started) .unwrap_or_else(|e| { - // TODO: Use "{e}" syntax once MSRV bumps above 1.58. panic!( - "Failed to compute duration between {:?} and {:?}: {}", - meta.at, started, e, + "Failed to compute duration between {:?} and \ + {started:?}: {e}", + meta.at, ); }) .as_nanos() @@ -272,10 +265,10 @@ impl Json { meta.at .duration_since(started) .unwrap_or_else(|e| { - // TODO: Use "{e}" syntax once MSRV bumps above 1.58. panic!( - "Failed to compute duration between {:?} and {:?}: {}", - meta.at, started, e, + "Failed to compute duration between {:?} and \ + {started:?}: {e}", + meta.at, ); }) .as_nanos() @@ -534,7 +527,7 @@ impl Element { name: format!( "{}{}", rule.map(|r| format!("{} ", r.name)).unwrap_or_default(), - scenario.name.clone() + scenario.name.clone(), ), tags: scenario .tags diff --git a/src/writer/junit.rs b/src/writer/junit.rs index 79573f74..3f64228e 100644 --- a/src/writer/junit.rs +++ b/src/writer/junit.rs @@ -96,7 +96,6 @@ where { type Cli = Cli; - #[allow(clippy::unused_async)] // false positive: #[async_trait] async fn handle_event( &mut self, ev: parser::Result>>, @@ -115,12 +114,10 @@ where TestSuiteBuilder::new(&format!( "Feature: {}{}", &feat.name, - // TODO: Use "{path}" syntax once MSRV bumps above - // 1.58. feat.path .as_deref() .and_then(Path::to_str) - .map(|path| format!(": {}", path)) + .map(|path| format!(": {path}")) .unwrap_or_default(), )) .set_timestamp(meta.at.into()) @@ -136,21 +133,18 @@ where } Feature::Finished => { let suite = self.suit.take().unwrap_or_else(|| { - // TODO: Use "{WRAP_ADVICE}" syntax once MSRV bumps - // above 1.58. panic!( - "No `TestSuit` for `Feature` \"{}\"\n{}", - feat.name, WRAP_ADVICE, + "No `TestSuit` for `Feature` \"{}\"\n{WRAP_ADVICE}", + feat.name, ) }); self.report.add_testsuite(suite); } }, Ok((Cucumber::Finished, _)) => { - // TODO: Use "{e}" syntax once MSRV bumps above 1.58. self.report .write_xml(&mut self.output) - .unwrap_or_else(|e| panic!("Failed to write XML: {}", e)); + .unwrap_or_else(|e| panic!("Failed to write XML: {e}")); } } } @@ -230,9 +224,8 @@ impl JUnit { ( format!( "Feature{}", - // TODO: Use "{p}" syntax once MSRV bumps above 1.58. path.to_str() - .map(|p| format!(": {}", p)) + .map(|p| format!(": {p}")) .unwrap_or_default(), ), "Parser Error", @@ -241,11 +234,10 @@ impl JUnit { parser::Error::ExampleExpansion(err) => ( format!( "Feature: {}{}:{}", - // TODO: Use "{p}" syntax once MSRV bumps above 1.58. err.path .as_deref() .and_then(Path::to_str) - .map(|p| format!("{}:", p)) + .map(|p| format!("{p}:")) .unwrap_or_default(), err.pos.line, err.pos.col, @@ -295,11 +287,10 @@ impl JUnit { self.suit .as_mut() .unwrap_or_else(|| { - // TODO: Use "{WRAP_ADVICE}" syntax once MSRV bumps - // above 1.58. panic!( - "No `TestSuit` for `Scenario` \"{}\"\n{}", - sc.name, WRAP_ADVICE, + "No `TestSuit` for `Scenario` \"{}\"\n\ + {WRAP_ADVICE}", + sc.name, ) }) .add_testcase(case); @@ -331,10 +322,9 @@ impl JUnit { ) }) .unwrap_or_else(|| { - // TODO: Use "{WRAP_ADVICE}" syntax once MSRV bumps above 1.58. panic!( - "No events for `Scenario` \"{}\"\n{}", - sc.name, WRAP_ADVICE, + "No events for `Scenario` \"{}\"\n{WRAP_ADVICE}", + sc.name, ) }); @@ -343,11 +333,10 @@ impl JUnit { rule.map(|r| format!("Rule: {}: ", r.name)) .unwrap_or_default(), sc.name, - // TODO: Use "{path}" syntax once MSRV bumps above 1.58. feat.path .as_ref() .and_then(|p| p.to_str()) - .map(|path| format!("{}:", path)) + .map(|path| format!("{path}:")) .unwrap_or_default(), sc.position.line, sc.position.col, @@ -382,10 +371,10 @@ impl JUnit { .build() } Scenario::Finished => { - // TODO: Use "{WRAP_ADVICE}" syntax once MSRV bumps above 1.58. panic!( - "Duplicated `Finished` event for `Scenario`: \"{}\"\n{}", - sc.name, WRAP_ADVICE, + "Duplicated `Finished` event for `Scenario`: \"{}\"\n\ + {WRAP_ADVICE}", + sc.name, ); } }; @@ -405,8 +394,7 @@ impl JUnit { }) .collect::>() .unwrap_or_else(|e| { - // TODO: Use "{e}" syntax once MSRV bumps above 1.58. - panic!("Failed to write with `writer::Basic`: {}", e) + panic!("Failed to write with `writer::Basic`: {e}") }); case.set_system_out(&output); @@ -423,26 +411,22 @@ impl JUnit { sc: &gherkin::Scenario, ) -> Duration { let started_at = self.scenario_started_at.take().unwrap_or_else(|| { - // TODO: Use "{WRAP_ADVICE}" syntax once MSRV bumps above 1.58. panic!( - "No `Started` event for `Scenario` \"{}\"\n{}", - sc.name, WRAP_ADVICE, + "No `Started` event for `Scenario` \"{}\"\n{WRAP_ADVICE}", + sc.name, ) }); Duration::try_from(ended.duration_since(started_at).unwrap_or_else( |e| { - // TODO: Use "{e}" syntax once MSRV bumps above 1.58. panic!( - "Failed to compute duration between {:?} and {:?}: {}", - ended, started_at, e, + "Failed to compute duration between {ended:?} and \ + {started_at:?}: {e}", ) }, )) .unwrap_or_else(|e| { - // TODO: Use "{e}" syntax once MSRV bumps above 1.58. panic!( - "Cannot covert `std::time::Duration` to `time::Duration`: {}", - e, + "Cannot covert `std::time::Duration` to `time::Duration`: {e}", ) }) } diff --git a/src/writer/normalize.rs b/src/writer/normalize.rs index 6759254d..37862afb 100644 --- a/src/writer/normalize.rs +++ b/src/writer/normalize.rs @@ -591,11 +591,11 @@ impl FeatureQueue { /// [`Rule`]: gherkin::Rule fn rule_finished(&mut self, rule: Event>) { let (rule, meta) = rule.split(); - match self.queue.get_mut(&Either::Left(rule)).unwrap() { - Either::Left(ev) => { + match self.queue.get_mut(&Either::Left(rule)) { + Some(Either::Left(ev)) => { ev.finished(meta); } - Either::Right(_) => unreachable!(), + Some(Either::Right(_)) | None => unreachable!(), } } diff --git a/src/writer/out.rs b/src/writer/out.rs index 718dbfa7..bd42db25 100644 --- a/src/writer/out.rs +++ b/src/writer/out.rs @@ -155,9 +155,8 @@ pub trait WriteStrExt: io::Write { /// /// If this writer fails to write a special sequence. fn move_cursor_up(&mut self, n: usize) -> io::Result<()> { - // TODO: Use "{n}" syntax once MSRV bumps above 1.58. (n > 0) - .then(|| self.write_str(format!("\x1b[{}A", n))) + .then(|| self.write_str(format!("\x1b[{n}A"))) .unwrap_or(Ok(())) } @@ -168,9 +167,8 @@ pub trait WriteStrExt: io::Write { /// /// If this writer fails to write a special sequence. fn move_cursor_down(&mut self, n: usize) -> io::Result<()> { - // TODO: Use "{n}" syntax once MSRV bumps above 1.58. (n > 0) - .then(|| self.write_str(format!("\x1b[{}B", n))) + .then(|| self.write_str(format!("\x1b[{n}B"))) .unwrap_or(Ok(())) } diff --git a/src/writer/summarize.rs b/src/writer/summarize.rs index 6ba1126c..bbe5cfab 100644 --- a/src/writer/summarize.rs +++ b/src/writer/summarize.rs @@ -486,19 +486,10 @@ impl Styles { .then(|| self.err(", ")) .unwrap_or_default(); - // TODO: Use "{features}" syntax once MSRV bumps above 1.58. format!( - "{}\n{}\n{}{}{}\n{}{}\n{}{}{}", - self.bold(self.header("[Summary]")), - features, - rules, - scenarios, - scenarios_stats, - steps, - steps_stats, - parsing_errors, - comma, - hook_errors + "{summary}\n{features}\n{rules}{scenarios}{scenarios_stats}\n\ + {steps}{steps_stats}\n{parsing_errors}{comma}{hook_errors}", + summary = self.bold(self.header("[Summary]")), ) .trim_end_matches('\n') .to_owned() @@ -530,12 +521,10 @@ impl Styles { (!formatted.is_empty()) .then(|| { - // TODO: Use "{formatted}" syntax once MSRV bumps above 1.58. self.bold(format!( - " {}{}{}", + " {}{formatted}{}", self.bold("("), - formatted, - self.bold(")") + self.bold(")"), )) }) .unwrap_or_default() @@ -547,10 +536,8 @@ impl Styles { singular: impl Into>, num: usize, ) -> Cow<'static, str> { - // TODO: Use "{num}" syntax once MSRV bumps above 1.58. self.bold(format!( - "{} {}{}", - num, + "{num} {}{}", singular.into(), (num != 1).then(|| "s").unwrap_or_default(), )) diff --git a/tests/output.rs b/tests/output.rs index e3a73846..816ebc5f 100644 --- a/tests/output.rs +++ b/tests/output.rs @@ -94,8 +94,7 @@ impl Writer for DebugWriter { ), ); - // TODO: Use "{ev:?}" syntax once MSRV bumps above 1.58. - format!("{:?}", ev).into() + format!("{ev:?}").into() } Ok(Cucumber::Feature( feat, @@ -121,11 +120,9 @@ impl Writer for DebugWriter { ), ); - // TODO: Use "{ev:?}" syntax once MSRV bumps above 1.58. - format!("{:?}", ev).into() + format!("{ev:?}").into() } - // TODO: Use "{ev:?}" syntax once MSRV bumps above 1.58. - Ok(ev) => format!("{:?}", ev).into(), + Ok(ev) => format!("{ev:?}").into(), }; let without_span = SPAN_OR_PATH_RE.replace_all(ev.as_ref(), ""); @@ -173,22 +170,18 @@ mod spec { ); for file in files { - // TODO: Use "{file}" syntax once MSRV bumps above 1.58. let out = fs::read_to_string(format!( - "tests/features/output/{}.out", - file, + "tests/features/output/{file}.out", )) .unwrap_or_default() .lines() .collect::(); - // TODO: Use "{file}" syntax once MSRV bumps above 1.58. let normalized = World::cucumber() .with_writer(DebugWriter::default().normalized()) - .run(format!("tests/features/output/{}", file)) + .run(format!("tests/features/output/{file}")) .await; - // TODO: Use "{file}" syntax once MSRV bumps above 1.58. - assert_eq!(normalized.0, out, "file: {}", file); + assert_eq!(normalized.0, out, "file: {file}"); } } }