rstest_bdd/step_args.rs
1//! Helpers for struct-based step arguments parsed from pattern placeholders.
2//!
3//! The [`StepArgs`] trait marks structs whose fields should be populated from
4//! the textual captures produced by a step pattern. Implementations record the
5//! field names for diagnostics and provide conversion logic from the ordered
6//! vector of capture strings emitted by the wrapper. Deriving the trait via
7//! `rstest_bdd_macros::StepArgs` enforces the required `FromStr` bounds and
8//! surfaces parse failures as [`StepArgsError`] values.
9
10use std::fmt;
11
12/// Error returned when converting captured placeholder strings into a struct
13/// annotated with `#[derive(StepArgs)]` fails.
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct StepArgsError {
16 message: String,
17}
18
19impl StepArgsError {
20 /// Construct a new error with the provided message.
21 #[must_use]
22 pub fn new(message: impl Into<String>) -> Self {
23 Self {
24 message: message.into(),
25 }
26 }
27
28 /// Build an error describing a failed parse for `field` using `raw`.
29 #[must_use]
30 pub fn parse_failure(field: &'static str, raw: &str) -> Self {
31 Self::new(format!(
32 "failed to parse field '{field}' from value '{raw}'"
33 ))
34 }
35
36 /// Build an error describing a mismatch between expected and actual counts.
37 ///
38 /// The derive macro validates capture counts at compile time, but the
39 /// constructor remains available for manual implementations.
40 #[must_use]
41 pub fn count_mismatch(expected: usize, actual: usize) -> Self {
42 Self::new(format!(
43 "expected {expected} captured value(s) but received {actual}"
44 ))
45 }
46
47 /// Access the underlying error message.
48 #[must_use]
49 pub fn message(&self) -> &str {
50 &self.message
51 }
52}
53
54impl fmt::Display for StepArgsError {
55 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56 f.write_str(&self.message)
57 }
58}
59
60impl std::error::Error for StepArgsError {}
61
62/// Trait implemented by structs populated from placeholder captures.
63pub trait StepArgs: Sized {
64 /// Number of fields participating in the capture mapping.
65 const FIELD_COUNT: usize;
66 /// Field names in declaration order. Used for documentation and future
67 /// diagnostics.
68 const FIELD_NAMES: &'static [&'static str];
69
70 /// Convert the ordered capture strings into a populated struct.
71 ///
72 /// # Errors
73 /// Returns [`StepArgsError`] when the conversion fails (for example when a
74 /// field cannot be parsed into the requested type).
75 fn from_captures(values: Vec<String>) -> Result<Self, StepArgsError>;
76}
77
78#[cfg(test)]
79mod tests {
80 use super::StepArgsError;
81
82 #[test]
83 fn parse_failure_formats_message() {
84 let err = StepArgsError::parse_failure("count", "NaN");
85 assert_eq!(
86 err.to_string(),
87 "failed to parse field 'count' from value 'NaN'"
88 );
89 }
90
91 #[test]
92 fn count_mismatch_formats_message() {
93 let err = StepArgsError::count_mismatch(2, 1);
94 assert_eq!(
95 err.to_string(),
96 "expected 2 captured value(s) but received 1"
97 );
98 }
99}