rstest_bdd_patterns/
errors.rs

1//! Error types shared by the pattern parsing modules.
2
3use std::fmt;
4use thiserror::Error;
5
6/// Additional context for placeholder-related parsing errors.
7///
8/// # Examples
9/// ```
10/// use rstest_bdd_patterns::PlaceholderErrorInfo;
11/// let info = PlaceholderErrorInfo::new("invalid placeholder", 3, Some("value".into()));
12/// assert_eq!(info.placeholder.as_deref(), Some("value"));
13/// assert_eq!(info.position, 3);
14/// ```
15#[derive(Debug, Clone, PartialEq, Eq)]
16pub struct PlaceholderErrorInfo {
17    pub message: &'static str,
18    pub position: usize,
19    pub placeholder: Option<String>,
20}
21
22impl PlaceholderErrorInfo {
23    /// Create a new error description for a placeholder failure.
24    ///
25    /// # Examples
26    /// ```
27    /// use rstest_bdd_patterns::PlaceholderErrorInfo;
28    /// let info = PlaceholderErrorInfo::new("invalid", 1, None);
29    /// assert_eq!(info.message, "invalid");
30    /// ```
31    #[must_use]
32    pub fn new(message: &'static str, position: usize, placeholder: Option<String>) -> Self {
33        Self {
34            message,
35            position,
36            placeholder,
37        }
38    }
39}
40
41impl fmt::Display for PlaceholderErrorInfo {
42    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43        match &self.placeholder {
44            Some(name) => write!(
45                f,
46                "{} for placeholder `{}` at byte {} (zero-based)",
47                self.message, name, self.position
48            ),
49            None => write!(f, "{} at byte {} (zero-based)", self.message, self.position),
50        }
51    }
52}
53
54/// Errors surfaced while converting step patterns into regular expressions.
55///
56/// # Examples
57/// ```
58/// use rstest_bdd_patterns::{PatternError, PlaceholderErrorInfo};
59/// let info = PlaceholderErrorInfo::new("invalid", 2, Some("count".into()));
60/// let err = PatternError::Placeholder(info.clone());
61/// assert_eq!(err.to_string(), info.to_string());
62/// ```
63#[derive(Debug, Error)]
64pub enum PatternError {
65    #[error("{0}")]
66    Placeholder(PlaceholderErrorInfo),
67    #[error(transparent)]
68    Regex(#[from] regex::Error),
69}
70
71pub(crate) fn placeholder_error(
72    message: &'static str,
73    position: usize,
74    placeholder: Option<String>,
75) -> PatternError {
76    PatternError::Placeholder(PlaceholderErrorInfo::new(message, position, placeholder))
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82
83    #[test]
84    fn formats_placeholder_with_name() {
85        let info = PlaceholderErrorInfo::new("invalid", 4, Some("count".into()));
86        assert_eq!(
87            info.to_string(),
88            "invalid for placeholder `count` at byte 4 (zero-based)"
89        );
90    }
91
92    #[test]
93    fn formats_placeholder_without_name() {
94        let info = PlaceholderErrorInfo::new("oops", 1, None);
95        assert_eq!(info.to_string(), "oops at byte 1 (zero-based)");
96    }
97
98    #[test]
99    fn forwards_regex_error_display() {
100        let err = PatternError::Regex(regex::Error::Syntax("bad".into()));
101        assert_eq!(
102            err.to_string(),
103            regex::Error::Syntax("bad".into()).to_string()
104        );
105    }
106}