Skip to main content

ocpp_client/ocpp_2_0_1/
error.rs

1use crate::error::ProtocolError;
2use alloc::format;
3use alloc::string::{String, ToString};
4use core::fmt;
5use ocpp_types::v201::RpcErrorCode;
6use serde_json::{Value, json};
7
8/// An OCPP 2.0.1 CALLERROR: the RPC framework error code from
9/// [`ocpp_types::v201::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 OCPP2_0_1Error {
14    pub code: RpcErrorCode,
15    pub description: String,
16    pub details: Value,
17}
18
19/// The exact wire spelling for each code, per the OCPP-J 2.0.1 specification's RPC framework
20/// error code table - `RpcErrorCode`'s serde impl already produces this, but
21/// `ProtocolError::code` returns a borrowed `&str` rather than allocating, so this mirrors it
22/// as a `match` instead of going through serialization.
23fn wire_code(code: RpcErrorCode) -> &'static str {
24    match code {
25        RpcErrorCode::FormatViolation => "FormatViolation",
26        RpcErrorCode::GenericError => "GenericError",
27        RpcErrorCode::InternalError => "InternalError",
28        RpcErrorCode::MessageTypeNotSupported => "MessageTypeNotSupported",
29        RpcErrorCode::NotImplemented => "NotImplemented",
30        RpcErrorCode::NotSupported => "NotSupported",
31        RpcErrorCode::OccurrenceConstraintViolation => "OccurrenceConstraintViolation",
32        RpcErrorCode::PropertyConstraintViolation => "PropertyConstraintViolation",
33        RpcErrorCode::ProtocolError => "ProtocolError",
34        RpcErrorCode::RpcFrameworkError => "RpcFrameworkError",
35        RpcErrorCode::SecurityError => "SecurityError",
36        RpcErrorCode::TypeConstraintViolation => "TypeConstraintViolation",
37    }
38}
39
40impl ProtocolError for OCPP2_0_1Error {
41    fn code(&self) -> &str {
42        wire_code(self.code)
43    }
44
45    fn description(&self) -> &str {
46        &self.description
47    }
48
49    fn details(&self) -> &Value {
50        &self.details
51    }
52
53    fn not_implemented(action: &str) -> Self {
54        OCPP2_0_1Error {
55            code: RpcErrorCode::NotImplemented,
56            description: format!("Action '{action}' is not implemented"),
57            details: json!({}),
58        }
59    }
60
61    fn from_wire(code: &str, description: &str, details: Value) -> Self {
62        let code = match code {
63            "FormatViolation" => RpcErrorCode::FormatViolation,
64            "InternalError" => RpcErrorCode::InternalError,
65            "MessageTypeNotSupported" => RpcErrorCode::MessageTypeNotSupported,
66            "NotImplemented" => RpcErrorCode::NotImplemented,
67            "NotSupported" => RpcErrorCode::NotSupported,
68            "OccurrenceConstraintViolation" => RpcErrorCode::OccurrenceConstraintViolation,
69            "PropertyConstraintViolation" => RpcErrorCode::PropertyConstraintViolation,
70            "ProtocolError" => RpcErrorCode::ProtocolError,
71            "RpcFrameworkError" => RpcErrorCode::RpcFrameworkError,
72            "SecurityError" => RpcErrorCode::SecurityError,
73            "TypeConstraintViolation" => RpcErrorCode::TypeConstraintViolation,
74            _ => RpcErrorCode::GenericError,
75        };
76        OCPP2_0_1Error {
77            code,
78            description: description.to_string(),
79            details,
80        }
81    }
82}
83
84/// Answer a schema violation with the CALLERROR 2.0.1 names for it. Unlike 1.6J, 2.x spells
85/// `OccurrenceConstraintViolation` with both `r`s - see the 1.6 impl for why that matters.
86#[cfg(feature = "validate")]
87impl From<ocpp_types::validate::ValidationError> for OCPP2_0_1Error {
88    fn from(error: ocpp_types::validate::ValidationError) -> Self {
89        use ocpp_types::validate::ConstraintClass;
90
91        let code = match error.kind().constraint_class() {
92            ConstraintClass::Property => RpcErrorCode::PropertyConstraintViolation,
93            ConstraintClass::Occurrence => RpcErrorCode::OccurrenceConstraintViolation,
94        };
95        let (description, details) = crate::error::validation_error_parts(&error);
96        OCPP2_0_1Error {
97            code,
98            description,
99            details,
100        }
101    }
102}
103
104impl fmt::Display for OCPP2_0_1Error {
105    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106        write!(f, "{}: {}", self.code(), self.description())
107    }
108}
109
110impl core::error::Error for OCPP2_0_1Error {}