Skip to main content

ocpp_client/ocpp_1_6/
error.rs

1use crate::error::ProtocolError;
2use alloc::format;
3use alloc::string::{String, ToString};
4use core::fmt;
5use ocpp_types::v16::RpcErrorCode;
6use serde_json::{Value, json};
7
8/// An OCPP 1.6 CALLERROR: the RPC framework error code from
9/// [`ocpp_types::v16::RpcErrorCode`], paired with the free-text description and details a
10/// CALLERROR frame carries alongside it (the spec leaves `errorDetails`'s shape undefined, so
11/// this crate keeps it as a raw `Value` rather than a typed field).
12#[derive(Debug, Clone)]
13pub struct OCPP1_6Error {
14    pub code: RpcErrorCode,
15    pub description: String,
16    pub details: Value,
17}
18
19/// The exact wire spelling for each code, per Table 7 of the OCPP-J 1.6 specification -
20/// `RpcErrorCode`'s serde impl already produces this, but `ProtocolError::code` returns a
21/// borrowed `&str` rather than allocating, so this mirrors it as a `match` instead of going
22/// through serialization.
23fn wire_code(code: RpcErrorCode) -> &'static str {
24    match code {
25        RpcErrorCode::NotImplemented => "NotImplemented",
26        RpcErrorCode::NotSupported => "NotSupported",
27        RpcErrorCode::InternalError => "InternalError",
28        RpcErrorCode::ProtocolError => "ProtocolError",
29        RpcErrorCode::SecurityError => "SecurityError",
30        RpcErrorCode::FormationViolation => "FormationViolation",
31        RpcErrorCode::PropertyConstraintViolation => "PropertyConstraintViolation",
32        RpcErrorCode::OccurenceConstraintViolation => "OccurenceConstraintViolation",
33        RpcErrorCode::TypeConstraintViolation => "TypeConstraintViolation",
34        RpcErrorCode::GenericError => "GenericError",
35    }
36}
37
38impl ProtocolError for OCPP1_6Error {
39    fn code(&self) -> &str {
40        wire_code(self.code)
41    }
42
43    fn description(&self) -> &str {
44        &self.description
45    }
46
47    fn details(&self) -> &Value {
48        &self.details
49    }
50
51    fn not_implemented(action: &str) -> Self {
52        OCPP1_6Error {
53            code: RpcErrorCode::NotImplemented,
54            description: format!("Action '{action}' is not implemented"),
55            details: json!({}),
56        }
57    }
58
59    fn from_wire(code: &str, description: &str, details: Value) -> Self {
60        let code = match code {
61            "NotImplemented" => RpcErrorCode::NotImplemented,
62            "NotSupported" => RpcErrorCode::NotSupported,
63            "InternalError" => RpcErrorCode::InternalError,
64            "ProtocolError" => RpcErrorCode::ProtocolError,
65            "SecurityError" => RpcErrorCode::SecurityError,
66            "FormationViolation" => RpcErrorCode::FormationViolation,
67            "PropertyConstraintViolation" => RpcErrorCode::PropertyConstraintViolation,
68            "OccurenceConstraintViolation" => RpcErrorCode::OccurenceConstraintViolation,
69            "TypeConstraintViolation" => RpcErrorCode::TypeConstraintViolation,
70            _ => RpcErrorCode::GenericError,
71        };
72        OCPP1_6Error {
73            code,
74            description: description.to_string(),
75            details,
76        }
77    }
78}
79
80impl fmt::Display for OCPP1_6Error {
81    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82        write!(f, "{}: {}", self.code(), self.description())
83    }
84}
85
86impl core::error::Error for OCPP1_6Error {}