Skip to main content

worker_matcher/
error.rs

1//! Error types for worker-matcher operations.
2//!
3//! The crate uses a single sum-type, [`MatchingError`], for every fallible
4//! operation, and a [`Result`] alias to keep call-sites concise.
5//!
6//! The matching engine itself is **infallible**: scoring two workers always
7//! produces a [`crate::MatchResult`]. Errors arise from explicit validation
8//! steps such as [`crate::Worker::validate`].
9//!
10//! ## Example
11//!
12//! ```
13//! use worker_matcher::{MatchingError, Worker};
14//!
15//! let empty = Worker::builder().build();
16//! match empty.validate() {
17//!     Err(MatchingError::MissingField(msg)) => {
18//!         assert!(msg.contains("required"));
19//!     }
20//!     other => panic!("unexpected: {other:?}"),
21//! }
22//! ```
23
24use thiserror::Error;
25
26/// Result alias used throughout the crate.
27///
28/// Equivalent to `std::result::Result<T, MatchingError>`.
29///
30/// ```
31/// use worker_matcher::Result;
32/// fn doubled(x: i32) -> Result<i32> { Ok(x * 2) }
33/// assert_eq!(doubled(3).unwrap(), 6);
34/// ```
35pub type Result<T> = std::result::Result<T, MatchingError>;
36
37/// Errors that may be returned by worker-matcher operations.
38///
39/// The matching engine itself is infallible — scoring two workers always
40/// produces a [`crate::MatchResult`]. The only fallible operation in the
41/// public surface today is [`crate::Worker::validate`], which returns
42/// [`MatchingError::MissingField`] when neither a name nor an identifier
43/// is populated. Identifier parsers in [`crate::identifiers`] return
44/// `Option<String>` (the parser is the source of truth on validity), so
45/// they never surface as errors. Configuration builders
46/// ([`crate::MatchConfig::default`], `strict`, `lenient`) are infallible.
47///
48/// The enum is `#[non_exhaustive]` so future fallible code paths can add
49/// variants without breaking SemVer for downstream pattern-matches.
50///
51/// ```
52/// use worker_matcher::MatchingError;
53///
54/// let e = MatchingError::MissingField("nhs_number".into());
55/// // `Display` is provided by `thiserror`.
56/// assert!(e.to_string().contains("Missing required field"));
57/// ```
58#[derive(Error, Debug)]
59#[non_exhaustive]
60pub enum MatchingError {
61    /// A required field was absent. Returned by [`crate::Worker::validate`].
62    #[error("Missing required field: {0}")]
63    MissingField(String),
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69
70    #[test]
71    fn missing_field_display() {
72        let e = MatchingError::MissingField("nhs_number".into());
73        assert_eq!(e.to_string(), "Missing required field: nhs_number");
74    }
75
76    #[test]
77    fn result_alias_resolves() {
78        fn make() -> Result<i32> {
79            Ok(42)
80        }
81        assert_eq!(make().unwrap(), 42);
82    }
83
84    #[test]
85    fn errors_are_send_and_sync() {
86        fn assert_send_sync<T: Send + Sync>() {}
87        assert_send_sync::<MatchingError>();
88    }
89}