Skip to main content

vr_jcs/
error.rs

1//! Error types for canonical JSON operations.
2
3use crate::MAX_NESTING_DEPTH;
4
5/// Error type for canonical JSON operations.
6#[derive(Debug)]
7#[non_exhaustive]
8pub enum JcsError {
9    /// JSON serialization or deserialization failed.
10    Json(serde_json::Error),
11    /// A JSON string violated I-JSON constraints.
12    InvalidString(String),
13    /// A JSON number violated JCS / I-JSON constraints.
14    InvalidNumber(String),
15    /// The input exceeded [`MAX_NESTING_DEPTH`].
16    NestingDepthExceeded,
17    /// A digest algorithm variant was requested but is not wired in this
18    /// build. Carried so callers can distinguish "algorithm is declared but
19    /// unimplemented here" from "algorithm is wrong".
20    UnsupportedAlgorithm(String),
21}
22
23impl std::fmt::Display for JcsError {
24    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25        match self {
26            Self::Json(e) => write!(f, "JCS JSON processing failed: {e}"),
27            Self::InvalidString(msg) => write!(f, "JCS string validation failed: {msg}"),
28            Self::InvalidNumber(msg) => write!(f, "JCS number validation failed: {msg}"),
29            Self::NestingDepthExceeded => write!(
30                f,
31                "JCS nesting depth exceeded maximum of {MAX_NESTING_DEPTH}"
32            ),
33            Self::UnsupportedAlgorithm(name) => {
34                write!(f, "digest algorithm not wired in this build: {name}")
35            }
36        }
37    }
38}
39
40impl std::error::Error for JcsError {
41    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
42        match self {
43            Self::Json(e) => Some(e),
44            Self::InvalidString(_)
45            | Self::InvalidNumber(_)
46            | Self::NestingDepthExceeded
47            | Self::UnsupportedAlgorithm(_) => None,
48        }
49    }
50}
51
52impl From<serde_json::Error> for JcsError {
53    fn from(error: serde_json::Error) -> Self {
54        Self::Json(error)
55    }
56}
57
58/// Stable downstream projection of [`JcsError`].
59///
60/// Lets consumers map to their own error type without matching on
61/// `JcsError` directly. Intentionally exhaustive: any future `JcsError`
62/// variant collapses into [`JcsErrorInfo::Validation`] via
63/// [`std::fmt::Display`], so adding variants to `JcsError` cannot break
64/// downstream matches on `JcsErrorInfo`.
65#[derive(Debug)]
66pub enum JcsErrorInfo {
67    /// Structured `serde_json` error preserved as-is.
68    Json(serde_json::Error),
69    /// Any non-`Json` failure rendered as a message.
70    Validation(String),
71}
72
73impl JcsError {
74    /// Project into the stable [`JcsErrorInfo`] view.
75    #[must_use]
76    pub fn into_info(self) -> JcsErrorInfo {
77        match self {
78            Self::Json(err) => JcsErrorInfo::Json(err),
79            Self::InvalidString(msg)
80            | Self::InvalidNumber(msg)
81            | Self::UnsupportedAlgorithm(msg) => JcsErrorInfo::Validation(msg),
82            other => JcsErrorInfo::Validation(other.to_string()),
83        }
84    }
85}