Skip to main content

higher_graphen_core/
error.rs

1use serde::{Deserialize, Serialize};
2use std::fmt;
3
4/// Core-owned result type for fallible primitive APIs.
5pub type Result<T> = std::result::Result<T, CoreError>;
6
7/// Structured, machine-readable errors for core primitive boundaries.
8#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
9#[serde(tag = "code", rename_all = "snake_case")]
10pub enum CoreError {
11    /// An identifier was empty or malformed after normalization.
12    InvalidId {
13        /// The value supplied by the caller or serialized input.
14        value: String,
15        /// A diagnostic explanation for humans.
16        reason: String,
17    },
18    /// A confidence score was NaN, infinite, or outside the valid range.
19    InvalidConfidence {
20        /// The value supplied by the caller or serialized input.
21        value: String,
22        /// A diagnostic explanation for humans.
23        reason: String,
24    },
25    /// A source kind was not one of the stable core categories.
26    InvalidSourceKind {
27        /// The value supplied by the caller or serialized input.
28        value: String,
29        /// A diagnostic explanation for humans.
30        reason: String,
31    },
32    /// A required field was absent or malformed at a primitive boundary.
33    MalformedField {
34        /// The stable lower snake case field name.
35        field: String,
36        /// A diagnostic explanation for humans.
37        reason: String,
38    },
39    /// A primitive parser could not convert input into the requested type.
40    ParseFailure {
41        /// The primitive type or target schema being parsed.
42        target: String,
43        /// The value supplied by the caller or serialized input.
44        value: String,
45        /// A diagnostic explanation for humans.
46        reason: String,
47    },
48    /// Serialized data used a version unsupported by this crate.
49    UnsupportedVersion {
50        /// The unsupported serialized version.
51        version: String,
52        /// The version or version range supported by this crate.
53        supported: String,
54    },
55}
56
57impl CoreError {
58    /// Returns the stable error code for bindings and tests.
59    pub fn code(&self) -> &'static str {
60        match self {
61            Self::InvalidId { .. } => "invalid_id",
62            Self::InvalidConfidence { .. } => "invalid_confidence",
63            Self::InvalidSourceKind { .. } => "invalid_source_kind",
64            Self::MalformedField { .. } => "malformed_field",
65            Self::ParseFailure { .. } => "parse_failure",
66            Self::UnsupportedVersion { .. } => "unsupported_version",
67        }
68    }
69
70    pub(crate) fn invalid_id(value: impl Into<String>, reason: impl Into<String>) -> Self {
71        Self::InvalidId {
72            value: value.into(),
73            reason: reason.into(),
74        }
75    }
76
77    pub(crate) fn invalid_confidence(value: f64, reason: impl Into<String>) -> Self {
78        Self::InvalidConfidence {
79            value: value.to_string(),
80            reason: reason.into(),
81        }
82    }
83
84    pub(crate) fn invalid_source_kind(value: impl Into<String>, reason: impl Into<String>) -> Self {
85        Self::InvalidSourceKind {
86            value: value.into(),
87            reason: reason.into(),
88        }
89    }
90
91    pub(crate) fn malformed_field(field: impl Into<String>, reason: impl Into<String>) -> Self {
92        Self::MalformedField {
93            field: field.into(),
94            reason: reason.into(),
95        }
96    }
97
98    pub(crate) fn parse_failure(
99        target: impl Into<String>,
100        value: impl Into<String>,
101        reason: impl Into<String>,
102    ) -> Self {
103        Self::ParseFailure {
104            target: target.into(),
105            value: value.into(),
106            reason: reason.into(),
107        }
108    }
109}
110
111impl fmt::Display for CoreError {
112    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
113        match self {
114            Self::InvalidId { value, reason } => {
115                write!(formatter, "{}: invalid id {value:?}: {reason}", self.code())
116            }
117            Self::InvalidConfidence { value, reason } => write!(
118                formatter,
119                "{}: invalid confidence {value:?}: {reason}",
120                self.code()
121            ),
122            Self::InvalidSourceKind { value, reason } => write!(
123                formatter,
124                "{}: invalid source kind {value:?}: {reason}",
125                self.code()
126            ),
127            Self::MalformedField { field, reason } => {
128                write!(
129                    formatter,
130                    "{}: malformed field {field:?}: {reason}",
131                    self.code()
132                )
133            }
134            Self::ParseFailure {
135                target,
136                value,
137                reason,
138            } => write!(
139                formatter,
140                "{}: could not parse {value:?} as {target}: {reason}",
141                self.code()
142            ),
143            Self::UnsupportedVersion { version, supported } => write!(
144                formatter,
145                "{}: unsupported version {version:?}; supported {supported}",
146                self.code()
147            ),
148        }
149    }
150}
151
152impl std::error::Error for CoreError {}