Skip to main content

moddef_core/
error.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! Typed errors (spec §26.3/§26.4, §32). Structured variants rather than
4//! strings; `Display` always, `std::error::Error` under `std`.
5
6use core::fmt;
7
8/// Codec decode failure causes.
9#[derive(Clone, Copy, Debug, PartialEq, Eq)]
10pub enum DecodeError {
11    /// scale_ref / selector_ref target not resolved in the context.
12    UnresolvedRef,
13    ZeroScaleDenominator,
14    ComposedBaseZero,
15    /// Register slice shorter than the point's width.
16    ShortRead,
17    /// Output buffer too small (string decode).
18    BufferTooSmall,
19    InvalidUtf8,
20}
21
22#[derive(Clone, Copy, Debug, PartialEq, Eq)]
23pub enum EncodeError {
24    NotWritable,
25    UnresolvedRef,
26    /// Composed / packed-field windows are read-oriented (§14, §13.1).
27    Unsupported,
28    WrongValueType,
29    BufferTooSmall,
30}
31
32/// §11.4 constraint that a write value violated.
33#[derive(Clone, Copy, Debug, PartialEq, Eq)]
34pub enum ConstraintKind {
35    Min,
36    Max,
37    Step,
38    AllowedValues,
39}
40
41/// Facade error, generic over the transport's error type.
42#[derive(Debug)]
43pub enum Error<T> {
44    Transport(T),
45    DeviceNotFound,
46    PointNotFound,
47    MeasurandNotSupported,
48    /// More than one point matches the measurand query (spec §26.4).
49    AmbiguousMeasurand,
50    /// Composed points via facade, unknown discovery kind, SunS not found…
51    UnsupportedMapping(&'static str),
52    Decode(DecodeError),
53    Encode(EncodeError),
54    WriteAccess,
55    WriteConstraint(ConstraintKind),
56    /// The command id does not exist in the device profile (§11.7).
57    CommandNotFound,
58    /// A required command param was not supplied to `run_command` (§11.7).
59    RequiredParamMissing,
60    /// A poll step exceeded its `timeout_ms` (§11.7).
61    PollTimeout,
62    /// A command step/result reference does not resolve (§11.7).
63    StepReference,
64}
65
66impl<T> From<DecodeError> for Error<T> {
67    fn from(e: DecodeError) -> Self {
68        Error::Decode(e)
69    }
70}
71
72impl<T> From<EncodeError> for Error<T> {
73    fn from(e: EncodeError) -> Self {
74        Error::Encode(e)
75    }
76}
77
78impl fmt::Display for DecodeError {
79    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80        match self {
81            DecodeError::UnresolvedRef => write!(f, "scale/selector ref not resolved in context"),
82            DecodeError::ZeroScaleDenominator => write!(f, "scale denominator is zero"),
83            DecodeError::ComposedBaseZero => write!(f, "composed base is zero"),
84            DecodeError::ShortRead => write!(f, "register window shorter than point width"),
85            DecodeError::BufferTooSmall => write!(f, "output buffer too small"),
86            DecodeError::InvalidUtf8 => write!(f, "decoded string is not valid UTF-8"),
87        }
88    }
89}
90
91impl fmt::Display for EncodeError {
92    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93        match self {
94            EncodeError::NotWritable => write!(f, "point is not writable"),
95            EncodeError::UnresolvedRef => write!(f, "scale ref not resolved in context"),
96            EncodeError::Unsupported => write!(f, "value kind is not encodable"),
97            EncodeError::WrongValueType => write!(f, "value type does not match the point"),
98            EncodeError::BufferTooSmall => write!(f, "register buffer too small"),
99        }
100    }
101}
102
103impl fmt::Display for ConstraintKind {
104    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105        match self {
106            ConstraintKind::Min => write!(f, "min_value"),
107            ConstraintKind::Max => write!(f, "max_value"),
108            ConstraintKind::Step => write!(f, "step"),
109            ConstraintKind::AllowedValues => write!(f, "allowed_values"),
110        }
111    }
112}
113
114impl<T: fmt::Display> fmt::Display for Error<T> {
115    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116        match self {
117            Error::Transport(e) => write!(f, "transport: {e}"),
118            Error::DeviceNotFound => write!(f, "device profile not found"),
119            Error::PointNotFound => write!(f, "point not found"),
120            Error::MeasurandNotSupported => write!(f, "measurand not supported"),
121            Error::AmbiguousMeasurand => write!(f, "measurand query is ambiguous"),
122            Error::UnsupportedMapping(d) => write!(f, "unsupported mapping: {d}"),
123            Error::Decode(e) => write!(f, "decode: {e}"),
124            Error::Encode(e) => write!(f, "encode: {e}"),
125            Error::WriteAccess => write!(f, "point is not writable"),
126            Error::WriteConstraint(k) => write!(f, "write violates constraint {k}"),
127            Error::CommandNotFound => write!(f, "command not found"),
128            Error::RequiredParamMissing => write!(f, "required command param missing"),
129            Error::PollTimeout => write!(f, "poll step timed out"),
130            Error::StepReference => write!(f, "command step reference not found"),
131        }
132    }
133}
134
135#[cfg(feature = "std")]
136impl std::error::Error for DecodeError {}
137#[cfg(feature = "std")]
138impl std::error::Error for EncodeError {}
139#[cfg(feature = "std")]
140impl<T: fmt::Display + fmt::Debug> std::error::Error for Error<T> {}