Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ output-json = [
"dep:mime",
"dep:serde",
"dep:serde_json",
"dep:serde_with",
"timestamps",
]
# Enables support for outputting JUnit XML report.
Expand All @@ -65,7 +66,7 @@ tracing = ["dep:crossbeam-utils", "dep:tracing", "dep:tracing-subscriber"]
[dependencies]
clap = { version = "4.3.2", features = ["derive", "wrap_help"] }
console = "0.15"
derive_more = { version = "0.99.17", features = ["as_ref", "deref", "deref_mut", "display", "error", "from", "from_str", "into"], default-features = false }
derive_more = { version = "2.0", features = ["as_ref", "debug", "deref", "deref_mut", "display", "error", "from", "from_str", "into"] }
drain_filter_polyfill = "0.1.2"
either = "1.6"
futures = "0.3.17"
Expand All @@ -92,6 +93,7 @@ Inflector = { version = "0.11", default-features = false, optional = true }
mime = { version = "0.3.16", optional = true }
serde = { version = "1.0.157", features = ["derive"], optional = true }
serde_json = { version = "1.0.18", optional = true }
serde_with = { version = "3.0", optional = true }

# "output-junit" feature dependencies.
junit-report = { version = "0.8", optional = true }
Expand All @@ -102,7 +104,6 @@ tracing = { version = "0.1", optional = true }
tracing-subscriber = { version = "0.3.16", optional = true }

[dev-dependencies]
derive_more = "0.99.17"
rand = "0.9"
tempfile = "3.2"
tokio = { version = "1.40", features = ["macros", "rt-multi-thread", "sync", "time"] }
Expand Down
2 changes: 1 addition & 1 deletion codegen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ synthez = "0.3"

[dev-dependencies]
cucumber = { path = "..", features = ["libtest", "macros"] }
derive_more = "0.99.17"
derive_more = { version = "2.0", features = ["deref", "from_str"] }
futures = "0.3.17"
tempfile = "3.2"
tokio = { version = "1.12", features = ["macros", "rt-multi-thread", "time"] }
Expand Down
36 changes: 6 additions & 30 deletions src/cucumber.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,9 @@
//!
//! [Cucumber]: https://cucumber.io

use std::{
borrow::Cow,
fmt::{self, Debug},
marker::PhantomData,
mem,
path::Path,
time::Duration,
};
use std::{borrow::Cow, marker::PhantomData, mem, path::Path, time::Duration};

use derive_more::with_trait::Debug;
use futures::{future::LocalBoxFuture, StreamExt as _};
use gherkin::tagexpr::TagOperation;
use regex::Regex;
Expand All @@ -40,7 +34,7 @@ use crate::{
/// [`World::run()`] or [`World::cucumber()`] on your [`World`] deriver to get
/// [Cucumber] up and running.
///
/// Otherwise use [`Cucumber::new()`] to get the default [Cucumber] executor,
/// Otherwise, use [`Cucumber::new()`] to get the default [Cucumber] executor,
/// provide [`Step`]s with [`World::collection()`] or by hand with
/// [`Cucumber::given()`], [`Cucumber::when()`] and [`Cucumber::then()`].
///
Expand All @@ -50,6 +44,7 @@ use crate::{
/// [`Cucumber::with_writer()`] to construct your dream [Cucumber] executor!
///
/// [Cucumber]: https://cucumber.io
#[derive(Debug)]
pub struct Cucumber<W, P, I, R, Wr, Cli = cli::Empty>
where
W: World,
Expand Down Expand Up @@ -77,9 +72,11 @@ where
cli: Option<cli::Opts<P::Cli, R::Cli, Wr::Cli, Cli>>,

/// Type of the [`World`] this [`Cucumber`] run on.
#[debug(ignore)]
_world: PhantomData<W>,

/// Type of the input consumed by [`Cucumber::parser`].
#[debug(ignore)]
_parser_input: PhantomData<I>,
}

Expand Down Expand Up @@ -814,27 +811,6 @@ where
}
}

impl<W, P, I, R, Wr, Cli> Debug for Cucumber<W, P, I, R, Wr, Cli>
where
W: World,
P: Debug + Parser<I>,
<P as Parser<I>>::Cli: Debug,
R: Debug + Runner<W>,
<R as Runner<W>>::Cli: Debug,
Wr: Debug + Writer<W>,
<Wr as Writer<W>>::Cli: Debug,
Cli: clap::Args + Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Cucumber")
.field("parser", &self.parser)
.field("runner", &self.runner)
.field("writer", &self.writer)
.field("cli", &self.cli)
.finish()
}
}

/// Shortcut for the [`Cucumber`] type returned by its [`Default`] impl.
pub(crate) type DefaultCucumber<W, I> = Cucumber<
W,
Expand Down
31 changes: 11 additions & 20 deletions src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,13 @@
use std::time::SystemTime;
use std::{
any::Any,
fmt,
hash::{Hash, Hasher},
sync::Arc,
};

use derive_more::{AsRef, Deref, DerefMut, Display, Error, From, Into};
use derive_more::with_trait::{
AsRef, Debug, Deref, DerefMut, Display, Error, From, Into,
};
use ref_cast::RefCast;

use crate::{step, writer::basic::coerce_error};
Expand Down Expand Up @@ -409,28 +410,29 @@ pub enum StepError {
///
/// [`Regex`]: regex::Regex
/// [`fail_on_skipped()`]: crate::WriterExt::fail_on_skipped()
#[display(fmt = "Step doesn't match any function")]
#[display("Step doesn't match any function")]
NotFound,

/// [`Step`] matches multiple [`Regex`]es.
///
/// [`Regex`]: regex::Regex
/// [`Step`]: gherkin::Step
#[display(fmt = "Step match is ambiguous: {}", _0)]
#[display("Step match is ambiguous: {_0}")]
AmbiguousMatch(step::AmbiguousMatchError),

/// [`Step`] panicked.
///
/// [`Step`]: gherkin::Step
#[display(fmt = "Step panicked. Captured output: {}", "coerce_error(_0)")]
#[display("Step panicked. Captured output: {}", coerce_error(_0))]
Panic(#[error(not(source))] Info),
}

/// Type of hook executed before or after all [`Scenario`]'s [`Step`]s.
///
/// [`Scenario`]: gherkin::Scenario
/// [`Step`]: gherkin::Step
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, Display)]
#[display("{self:?}")]
pub enum HookType {
/// Executing on each [`Scenario`] before running all [`Step`]s.
///
Expand All @@ -445,13 +447,6 @@ pub enum HookType {
After,
}

#[expect(clippy::use_debug, reason = "intentional")]
impl fmt::Display for HookType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{self:?}")
}
}

/// Event of running [`Before`] or [`After`] hook.
///
/// [`After`]: HookType::After
Expand Down Expand Up @@ -713,8 +708,10 @@ pub enum ScenarioFinished {
/// Wrappers around a [`gherkin`] type ([`gherkin::Feature`],
/// [`gherkin::Scenario`], etc.), providing cheap [`Clone`], [`Hash`] and
/// [`PartialEq`] implementations for using it extensively in [`Event`]s.
#[derive(AsRef, Deref, Display, From, Into, RefCast)]
#[derive(AsRef, Debug, Deref, Display, From, Into, RefCast)]
#[as_ref(forward)]
#[debug("{:?}", **_0)]
#[debug(bound(T: Debug))]
#[deref(forward)]
#[repr(transparent)]
pub struct Source<T: ?Sized>(Arc<T>);
Expand All @@ -727,12 +724,6 @@ impl<T> Source<T> {
}
}

impl<T: fmt::Debug + ?Sized> fmt::Debug for Source<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}

// Manual implementation is required to omit the redundant `T: Clone` trait
// bound imposed by `#[derive(Clone)]`.
impl<T> Clone for Source<T> {
Expand Down
11 changes: 5 additions & 6 deletions src/feature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use std::{
sync::LazyLock,
};

use derive_more::{Display, Error};
use derive_more::with_trait::{Display, Error};
use regex::Regex;
use sealed::sealed;

Expand Down Expand Up @@ -231,11 +231,10 @@ fn expand_scenario(
/// [1]: https://cucumber.io/docs/gherkin/reference#scenario-outline
#[derive(Clone, Debug, Display, Error)]
#[display(
fmt = "Failed to resolve <{}> at {}:{}:{}",
name,
"path.as_deref().and_then(Path::to_str).map(trim_path).unwrap_or_default()",
"pos.line",
"pos.col"
"Failed to resolve <{name}> at {}:{}:{}",
path.as_deref().and_then(Path::to_str).map(trim_path).unwrap_or_default(),
pos.line,
pos.col,
)]
pub struct ExpandExamplesError {
/// Position of the unknown template.
Expand Down
2 changes: 0 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,6 @@
unused_results,
variant_size_differences
)]
// TODO: Remove on next `derive_more` major version.
#![expect(clippy::uninlined_format_args, reason = "`derive_more` expansion")]

pub mod cli;
mod cucumber;
Expand Down
4 changes: 2 additions & 2 deletions src/parser/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use std::{
vec,
};

use derive_more::{Display, Error};
use derive_more::with_trait::{Display, Error};
use futures::stream;
use gherkin::GherkinEnv;
use globwalk::{GlobWalker, GlobWalkerBuilder};
Expand Down Expand Up @@ -164,7 +164,7 @@ impl Basic {

/// Error of [`gherkin`] not supporting keywords in some language.
#[derive(Clone, Debug, Display, Error)]
#[display(fmt = "Language {} isn't supported", _0)]
#[display("Language {_0} isn't supported")]
pub struct UnsupportedLanguageError(
#[error(not(source))] pub Cow<'static, str>,
);
Expand Down
8 changes: 4 additions & 4 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pub mod basic;

use std::sync::Arc;

use derive_more::{Display, Error};
use derive_more::with_trait::{Display, Error as StdError};
use futures::Stream;

use crate::feature::ExpandExamplesError;
Expand Down Expand Up @@ -57,18 +57,18 @@ pub trait Parser<I> {
pub type Result<T> = std::result::Result<T, Error>;

/// [`Parser`] error.
#[derive(Clone, Debug, Display, Error)]
#[derive(Clone, Debug, Display, StdError)]
pub enum Error {
/// Failed to parse a [`Feature`].
///
/// [`Feature`]: gherkin::Feature
#[display(fmt = "Failed to parse feature: {}", _0)]
#[display("Failed to parse feature: {_0}")]
Parsing(Arc<gherkin::ParseFileError>),

/// Failed to expand [`Examples`]
///
/// [`Examples`]: gherkin::Examples
#[display(fmt = "Failed to expand examples: {}", _0)]
#[display("Failed to expand examples: {_0}")]
ExampleExpansion(Arc<ExpandExamplesError>),
}

Expand Down
25 changes: 8 additions & 17 deletions src/runner/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use std::{
any::Any,
cmp,
collections::HashMap,
fmt, iter, mem,
iter, mem,
ops::ControlFlow,
panic::{self, AssertUnwindSafe},
sync::{
Expand All @@ -27,7 +27,7 @@ use std::{

#[cfg(feature = "tracing")]
use crossbeam_utils::atomic::AtomicCell;
use derive_more::{Display, FromStr};
use derive_more::with_trait::{Debug, Display, FromStr};
use drain_filter_polyfill::VecExt;
use futures::{
channel::{mpsc, oneshot},
Expand Down Expand Up @@ -323,6 +323,7 @@ type IsRetried = bool;
///
/// [1]: Runner#order-guarantees
/// [`Scenario`]: gherkin::Scenario
#[derive(Debug)]
pub struct Basic<
World,
F = WhichScenarioFn,
Expand Down Expand Up @@ -360,11 +361,13 @@ pub struct Basic<
/// [`Concurrent`]: ScenarioType::Concurrent
/// [`Serial`]: ScenarioType::Serial
/// [`Scenario`]: gherkin::Scenario
#[debug(ignore)]
which_scenario: F,

/// Function determining [`Scenario`]'s [`RetryOptions`].
///
/// [`Scenario`]: gherkin::Scenario
#[debug(ignore)]
retry_options: RetryOptionsFn,

/// Function, executed on each [`Scenario`] before running all [`Step`]s,
Expand All @@ -373,20 +376,23 @@ pub struct Basic<
/// [`Background`]: gherkin::Background
/// [`Scenario`]: gherkin::Scenario
/// [`Step`]: gherkin::Step
#[debug(ignore)]
before_hook: Option<Before>,

/// Function, executed on each [`Scenario`] after running all [`Step`]s.
///
/// [`Background`]: gherkin::Background
/// [`Scenario`]: gherkin::Scenario
/// [`Step`]: gherkin::Step
#[debug(ignore)]
after_hook: Option<After>,

/// Indicates whether execution should be stopped after the first failure.
fail_fast: bool,

#[cfg(feature = "tracing")]
/// [`TracingCollector`] for [`event::Scenario::Log`]s forwarding.
#[debug(ignore)]
pub(crate) logs_collector: Arc<AtomicCell<Box<Option<TracingCollector>>>>,
}

Expand Down Expand Up @@ -420,21 +426,6 @@ impl<World, F: Clone, B: Clone, A: Clone> Clone for Basic<World, F, B, A> {
}
}

// Implemented manually to omit redundant trait bounds on `World` and to omit
// outputting `F`.
impl<World, F, B, A> fmt::Debug for Basic<World, F, B, A> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Basic")
.field("max_concurrent_scenarios", &self.max_concurrent_scenarios)
.field("retries", &self.retries)
.field("retry_after", &self.retry_after)
.field("retry_filter", &self.retry_filter)
.field("steps", &self.steps)
.field("fail_fast", &self.fail_fast)
.finish_non_exhaustive()
}
}

impl<World> Default for Basic<World> {
fn default() -> Self {
let which_scenario: WhichScenarioFn = |feature, rule, scenario| {
Expand Down
Loading