Skip to main content

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}