Skip to main content

ocpp_client/
error.rs

1use serde_json::Value;
2
3/// Implemented once per OCPP version by that version's error enum (`OCPP1_6Error`,
4/// `OCPP2_0_1Error`, ...), so the generic [`crate::Client`] engine can build and read
5/// CALLERROR payloads without knowing which version it's carrying.
6pub trait ProtocolError: core::fmt::Debug + Send + Sync + Sized + 'static {
7    fn code(&self) -> &str;
8    fn description(&self) -> &str;
9    fn details(&self) -> &Value;
10    fn not_implemented(action: &str) -> Self;
11    fn from_wire(code: &str, description: &str, details: Value) -> Self;
12}
13
14/// Everything that can go wrong sending or receiving a single OCPP action, flattened into
15/// one type instead of the `Result<Result<Response, ProtocolError>, Box<dyn Error>>` shape.
16#[derive(Debug)]
17pub enum ClientError<E> {
18    /// The other side answered with a CALLERROR.
19    Protocol(E),
20    /// No CALLRESULT/CALLERROR arrived before the client's timeout elapsed.
21    Timeout,
22    /// The payload didn't match the expected request/response type.
23    Decode(serde_json::Error),
24    /// The transport failed to send or receive a frame.
25    Transport(crate::transport::TransportError),
26    /// The connection was closed before a response arrived.
27    Closed,
28}
29
30impl<E: ProtocolError> core::fmt::Display for ClientError<E> {
31    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
32        match self {
33            ClientError::Protocol(e) => {
34                write!(f, "protocol error: {} ({})", e.code(), e.description())
35            }
36            ClientError::Timeout => write!(f, "request timed out"),
37            ClientError::Decode(e) => write!(f, "failed to decode payload: {e}"),
38            ClientError::Transport(e) => write!(f, "transport error: {e}"),
39            ClientError::Closed => write!(f, "connection closed"),
40        }
41    }
42}
43
44impl<E: ProtocolError> core::error::Error for ClientError<E> {}