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}