cucumber/
codegen.rs

1// Copyright (c) 2018-2024  Brendan Molloy <brendan@bbqsrc.net>,
2//                          Ilya Solovyiov <ilya.solovyiov@gmail.com>,
3//                          Kai Ren <tyranron@gmail.com>
4//
5// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8// option. This file may not be copied, modified, or distributed
9// except according to those terms.
10
11//! Helper type-level glue for [`cucumber_codegen`] crate.
12
13use std::{convert::Infallible, future::Future};
14
15use futures::future;
16
17use crate::{step, Step, World};
18
19pub use anyhow;
20pub use cucumber_expressions::{
21    expand::parameters::Provider as ParametersProvider, Expression, Spanned,
22};
23pub use futures::future::LocalBoxFuture;
24pub use inventory::{self, collect, submit};
25pub use once_cell::sync::Lazy;
26pub use regex::Regex;
27
28/// [`World`] extension allowing to register steps in [`inventory`].
29pub trait WorldInventory: World {
30    /// Struct [`submit`]ted in a [`given`] macro.
31    ///
32    /// [`given`]: crate::given
33    type Given: inventory::Collect + StepConstructor<Self>;
34
35    /// Struct [`submit`]ted in a [`when`] macro.
36    ///
37    /// [`when`]: crate::when
38    type When: inventory::Collect + StepConstructor<Self>;
39
40    /// Struct [`submit`]ted in a [`then`] macro.
41    ///
42    /// [`then`]: crate::then
43    type Then: inventory::Collect + StepConstructor<Self>;
44}
45
46/// Alias for a [`fn`] returning a [`Lazy`] [`Regex`].
47pub type LazyRegex = fn() -> Regex;
48
49/// Trait for registering a [`Step`] with [`given`], [`when`] and [`then`]
50/// attributes inside [`World::collection()`] method.
51///
52/// [`given`]: crate::given
53/// [`when`]: crate::when
54/// [`then`]: crate::then
55pub trait StepConstructor<W> {
56    /// Returns an inner [`Step`] with the corresponding [`Regex`].
57    fn inner(&self) -> (step::Location, LazyRegex, Step<W>);
58}
59
60/// Custom parameter of a [Cucumber Expression].
61///
62/// Should be implemented only with via [`Parameter`] derive macro.
63///
64/// [`Parameter`]: macro@crate::Parameter
65/// [Cucumber Expression]: https://cucumber.github.io/cucumber-expressions
66pub trait Parameter {
67    /// [`Regex`] matching this [`Parameter`].
68    ///
69    /// Shouldn't contain any capturing groups.
70    ///
71    /// Validated during [`Parameter`](macro@crate::Parameter) derive macro
72    /// expansion.
73    const REGEX: &'static str;
74
75    /// Name of this [`Parameter`] to be referenced by in
76    /// [Cucumber Expressions].
77    ///
78    /// [Cucumber Expressions]: https://cucumber.github.io/cucumber-expressions
79    const NAME: &'static str;
80}
81
82/// Compares two strings in a `const` context.
83///
84/// As there is no `const impl Trait` and `l == r` calls [`Eq`], we have to use
85/// a custom comparison function.
86///
87/// [`Eq`]: std::cmp::Eq
88// TODO: Remove once `Eq` trait is allowed in a `const` context.
89#[must_use]
90pub const fn str_eq(l: &str, r: &str) -> bool {
91    if l.len() != r.len() {
92        return false;
93    }
94
95    let (l, r) = (l.as_bytes(), r.as_bytes());
96    let mut i = 0;
97    while i < l.len() {
98        if l[i] != r[i] {
99            return false;
100        }
101        i += 1;
102    }
103
104    true
105}
106
107/// Return-type polymorphism over `async`ness for a `#[world(init)]` attribute
108/// of a [`#[derive(World)]`](macro@World) macro.
109///
110/// It allows to accept both sync and `async` functions as an attribute's
111/// argument, by automatically wrapping sync functions in a [`future::Ready`].
112///
113/// ```rust
114/// # #[derive(Default)]
115/// # struct World;
116/// #
117/// impl cucumber::World for World {
118///     type Error = anyhow::Error;
119///
120///     async fn new() -> Result<Self, Self::Error> {
121///         use cucumber::codegen::{
122///             IntoWorldResult as _, ToWorldFuture as _,
123///         };
124///
125///         fn as_fn_ptr<T>(v: fn() -> T) -> fn() -> T {
126///             v
127///         }
128///
129///         //           `#[world(init)]`'s value
130///         //          ⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄
131///         (&as_fn_ptr(<Self as Default>::default))
132///             .to_world_future() // maybe wraps into `future::Ready`
133///             .await
134///             .into_world_result()
135///             .map_err(Into::into)
136///     }
137/// }
138/// ```
139pub trait ToWorldFuture {
140    /// [`Future`] returned by this [`World`] constructor.
141    ///
142    /// Set to [`future::Ready`] in case construction is sync.
143    type Future: Future;
144
145    /// Resolves this [`Future`] for constructing a new[`World`] using
146    /// [autoderef-based specialization][0].
147    ///
148    /// [0]: https://tinyurl.com/autoref-spec
149    fn to_world_future(&self) -> Self::Future;
150}
151
152impl<R: IntoWorldResult> ToWorldFuture for fn() -> R {
153    type Future = future::Ready<R>;
154
155    fn to_world_future(&self) -> Self::Future {
156        future::ready(self())
157    }
158}
159
160impl<Fut: Future> ToWorldFuture for &fn() -> Fut
161where
162    Fut::Output: IntoWorldResult,
163{
164    type Future = Fut;
165
166    fn to_world_future(&self) -> Self::Future {
167        self()
168    }
169}
170
171/// Return-type polymorphism over fallibility for a `#[world(init)]` attribute
172/// of a [`#[derive(World)]`](macro@World) macro.
173///
174/// It allows to accept both fallible (returning [`Result`]) and infallible
175/// functions as an attribute's argument, by automatically wrapping infallible
176/// functions in a [`Result`]`<`[`World`]`, `[`Infallible`]`>`.
177///
178/// ```rust
179/// # #[derive(Default)]
180/// # struct World;
181/// #
182/// impl cucumber::World for World {
183///     type Error = anyhow::Error;
184///
185///     async fn new() -> Result<Self, Self::Error> {
186///         use cucumber::codegen::{
187///             IntoWorldResult as _, ToWorldFuture as _,
188///         };
189///
190///         fn as_fn_ptr<T>(v: fn() -> T) -> fn() -> T {
191///             v
192///         }
193///
194///         //           `#[world(init)]`'s value
195///         //          ⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄
196///         (&as_fn_ptr(<Self as Default>::default))
197///             .to_world_future()
198///             .await
199///             .into_world_result() // maybe wraps into `Result<_, Infallible>`
200///             .map_err(Into::into)
201///     }
202/// }
203/// ```
204pub trait IntoWorldResult: Sized {
205    /// [`World`] type itself.
206    type World: World;
207
208    /// Error returned by this [`World`] constructor.
209    ///
210    /// Set to [`Infallible`] in case construction is infallible.
211    type Error;
212
213    /// Passes [`Result`]`<`[`World`]`, Self::Error>` as is, or wraps the plain
214    /// [`World`] in a [`Result`]`<`[`World`]`, `[`Infallible`]`>`.
215    ///
216    /// # Errors
217    ///
218    /// In case the [`World`] construction errors.
219    fn into_world_result(self) -> Result<Self::World, Self::Error>;
220}
221
222impl<W: World> IntoWorldResult for W {
223    type World = Self;
224    type Error = Infallible;
225
226    fn into_world_result(self) -> Result<Self, Self::Error> {
227        Ok(self)
228    }
229}
230
231impl<W: World, E> IntoWorldResult for Result<W, E> {
232    type World = W;
233    type Error = E;
234
235    fn into_world_result(self) -> Self {
236        self
237    }
238}