Skip to main content

scrollcase_consumer/
error.rs

1//! The single error path.
2//!
3//! Scrollcase has one rule for validation failures across all its implementations: every one of them
4//! produces one clear line, and there is no second error path to reason about. Node expresses that
5//! with `fail()`, Python with a single exception type, and this crate with one opaque struct.
6//!
7//! Deliberately *not* an enum of failure kinds. A caller that matched on variants would be writing
8//! its own interpretation of which rejections are equivalent, and every added variant would then be
9//! a breaking change to a security-relevant API. The message is the contract, and the conformance
10//! fixture pins the substrings that matter.
11
12use std::fmt;
13
14/// A validation or I/O failure, carrying the one line a caller should surface.
15#[derive(Debug)]
16pub struct Error {
17    message: String,
18}
19
20impl Error {
21    /// Builds an error from a message.
22    pub fn new(message: impl Into<String>) -> Self {
23        Self {
24            message: message.into(),
25        }
26    }
27
28    /// The failure message.
29    #[must_use]
30    pub fn message(&self) -> &str {
31        &self.message
32    }
33}
34
35impl fmt::Display for Error {
36    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
37        formatter.write_str(&self.message)
38    }
39}
40
41impl std::error::Error for Error {}
42
43impl From<std::io::Error> for Error {
44    fn from(error: std::io::Error) -> Self {
45        Self::new(error.to_string())
46    }
47}
48
49/// Result alias used throughout the crate.
50pub type Result<T> = std::result::Result<T, Error>;
51
52/// Builds an [`Error`] from a format string, mirroring Node's `fail()` call sites.
53macro_rules! fail {
54    ($($argument:tt)*) => {
55        return Err($crate::error::Error::new(format!($($argument)*)))
56    };
57}
58
59pub(crate) use fail;