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;