1use serde::Serialize;
15use serde_json::value::RawValue;
16
17use crate::{ErrorCode, ErrorDialect, RpcError};
18
19pub const MAX_MESSAGE_ID_LEN: usize = 36;
21pub const MAX_ERROR_DESCRIPTION_LEN: usize = 255;
23pub const UNKNOWN_MESSAGE_ID: &str = "-1";
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27#[repr(u8)]
28pub enum MessageTypeId {
29 Call = 2,
30 CallResult = 3,
31 CallError = 4,
32 CallResultError = 5,
33 Send = 6,
34}
35
36#[derive(Debug)]
37pub enum Frame {
38 Call {
39 id: String,
40 action: String,
41 payload: Box<RawValue>,
42 },
43 CallResult {
44 id: String,
45 payload: Box<RawValue>,
46 },
47 CallError {
48 id: String,
49 code: String,
50 description: String,
51 details: Box<RawValue>,
52 },
53 CallResultError {
54 id: String,
55 code: String,
56 description: String,
57 details: Box<RawValue>,
58 },
59 Send {
60 id: String,
61 action: String,
62 payload: Box<RawValue>,
63 },
64}
65
66#[derive(Debug, Clone, PartialEq)]
68pub enum FrameError {
69 Malformed { id: Option<String>, reason: String },
71 UnknownMessageType { id: Option<String>, type_id: u64 },
73}
74
75impl FrameError {
76 pub fn reply_id(&self) -> &str {
78 match self {
79 FrameError::Malformed { id, .. } | FrameError::UnknownMessageType { id, .. } => {
80 id.as_deref().unwrap_or(UNKNOWN_MESSAGE_ID)
81 }
82 }
83 }
84}
85
86impl std::fmt::Display for FrameError {
87 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88 match self {
89 FrameError::Malformed { reason, .. } => write!(f, "malformed OCPP-J frame: {reason}"),
90 FrameError::UnknownMessageType { type_id, .. } => {
91 write!(f, "unknown message type id {type_id}")
92 }
93 }
94 }
95}
96
97impl std::error::Error for FrameError {}
98
99fn empty_object() -> Box<RawValue> {
100 RawValue::from_string("{}".to_owned()).expect("valid json")
101}
102
103fn payload(raw: &RawValue) -> Box<RawValue> {
105 if raw.get() == "null" {
106 empty_object()
107 } else {
108 raw.to_owned()
109 }
110}
111
112fn string(raw: &RawValue) -> Option<String> {
113 serde_json::from_str::<String>(raw.get()).ok()
114}
115
116impl Frame {
117 pub fn parse(text: &str) -> Result<Frame, FrameError> {
118 let parts: Vec<&RawValue> =
119 serde_json::from_str(text).map_err(|e| FrameError::Malformed {
120 id: None,
121 reason: format!("not a JSON array: {e}"),
122 })?;
123 let type_id: u64 = parts
124 .first()
125 .and_then(|r| serde_json::from_str(r.get()).ok())
126 .ok_or_else(|| FrameError::Malformed {
127 id: None,
128 reason: "missing or invalid MessageTypeId".into(),
129 })?;
130 let id = parts.get(1).and_then(|r| string(r));
131 let Some(id_str) = id.clone() else {
132 return Err(FrameError::Malformed {
133 id: None,
134 reason: "missing or invalid MessageId".into(),
135 });
136 };
137 let bad = |reason: &str| FrameError::Malformed {
138 id: id.clone(),
139 reason: reason.into(),
140 };
141 let check_object = |r: &RawValue| -> Result<Box<RawValue>, FrameError> {
142 let p = payload(r);
143 if p.get().trim_start().starts_with('{') {
144 Ok(p)
145 } else {
146 Err(bad("payload is not a JSON object"))
147 }
148 };
149 match type_id {
150 2 | 6 => {
151 if parts.len() != 4 {
152 return Err(bad("CALL/SEND must have 4 elements"));
153 }
154 let action = string(parts[2]).ok_or_else(|| bad("Action is not a string"))?;
155 let payload = check_object(parts[3])?;
156 Ok(if type_id == 2 {
157 Frame::Call {
158 id: id_str,
159 action,
160 payload,
161 }
162 } else {
163 Frame::Send {
164 id: id_str,
165 action,
166 payload,
167 }
168 })
169 }
170 3 => {
171 if parts.len() != 3 {
172 return Err(bad("CALLRESULT must have 3 elements"));
173 }
174 Ok(Frame::CallResult {
175 id: id_str,
176 payload: check_object(parts[2])?,
177 })
178 }
179 4 | 5 => {
180 if !(4..=5).contains(&parts.len()) {
182 return Err(bad("CALLERROR must have 5 elements"));
183 }
184 let code = string(parts[2]).ok_or_else(|| bad("errorCode is not a string"))?;
185 let description = string(parts[3]).unwrap_or_default();
186 let details = parts
187 .get(4)
188 .map(|r| payload(r))
189 .unwrap_or_else(empty_object);
190 Ok(if type_id == 4 {
191 Frame::CallError {
192 id: id_str,
193 code,
194 description,
195 details,
196 }
197 } else {
198 Frame::CallResultError {
199 id: id_str,
200 code,
201 description,
202 details,
203 }
204 })
205 }
206 other => Err(FrameError::UnknownMessageType { id, type_id: other }),
207 }
208 }
209
210 pub fn id(&self) -> &str {
211 match self {
212 Frame::Call { id, .. }
213 | Frame::CallResult { id, .. }
214 | Frame::CallError { id, .. }
215 | Frame::CallResultError { id, .. }
216 | Frame::Send { id, .. } => id,
217 }
218 }
219}
220
221pub fn encode_call<P: Serialize + ?Sized>(
223 id: &str,
224 action: &str,
225 payload: &P,
226) -> Result<String, serde_json::Error> {
227 serde_json::to_string(&(MessageTypeId::Call as u8, id, action, payload))
228}
229
230pub fn encode_send<P: Serialize + ?Sized>(
232 id: &str,
233 action: &str,
234 payload: &P,
235) -> Result<String, serde_json::Error> {
236 serde_json::to_string(&(MessageTypeId::Send as u8, id, action, payload))
237}
238
239pub fn encode_result<P: Serialize + ?Sized>(
241 id: &str,
242 payload: &P,
243) -> Result<String, serde_json::Error> {
244 serde_json::to_string(&(MessageTypeId::CallResult as u8, id, payload))
245}
246
247pub fn encode_error(id: &str, err: &RpcError, dialect: ErrorDialect, result_error: bool) -> String {
249 let type_id = if result_error {
250 MessageTypeId::CallResultError
251 } else {
252 MessageTypeId::CallError
253 } as u8;
254 let mut description = err.description.as_str();
255 if description.chars().count() > MAX_ERROR_DESCRIPTION_LEN {
256 let cut = description
257 .char_indices()
258 .nth(MAX_ERROR_DESCRIPTION_LEN)
259 .map(|(i, _)| i)
260 .unwrap_or(description.len());
261 description = &description[..cut];
262 }
263 let empty = serde_json::Value::Object(Default::default());
265 let details = if err.details.is_object() {
266 &err.details
267 } else {
268 &empty
269 };
270 serde_json::to_string(&(type_id, id, err.code.as_wire(dialect), description, details))
271 .expect("error frame serializes")
272}
273
274pub fn error_from_frame(code: &str, description: String, details: &RawValue) -> RpcError {
276 RpcError {
277 code: ErrorCode::from_wire(code),
278 description,
279 details: serde_json::from_str(details.get()).unwrap_or_default(),
280 }
281}
282
283#[cfg(test)]
284mod tests {
285 use super::*;
286
287 #[test]
288 fn parse_call() {
289 let f = Frame::parse(r#"[2, "19223201", "BootNotification", {"chargePointVendor": "VendorX", "chargePointModel": "X"}]"#).unwrap();
290 match f {
291 Frame::Call {
292 id,
293 action,
294 payload,
295 } => {
296 assert_eq!(id, "19223201");
297 assert_eq!(action, "BootNotification");
298 assert!(payload.get().contains("VendorX"));
299 }
300 _ => panic!(),
301 }
302 let f = Frame::parse(r#"[2,"1","Heartbeat",null]"#).unwrap();
303 assert!(matches!(f, Frame::Call { ref payload, .. } if payload.get() == "{}"));
304 }
305
306 #[test]
307 fn parse_result_and_error() {
308 assert!(matches!(
309 Frame::parse(r#"[3,"a",{"status":"Accepted"}]"#).unwrap(),
310 Frame::CallResult { .. }
311 ));
312 match Frame::parse(
313 r#"[4,"162376037","NotSupported","SetDisplayMessageRequest not supported",{}]"#,
314 )
315 .unwrap()
316 {
317 Frame::CallError {
318 code, description, ..
319 } => {
320 assert_eq!(code, "NotSupported");
321 assert_eq!(description, "SetDisplayMessageRequest not supported");
322 }
323 _ => panic!(),
324 }
325 assert!(matches!(
326 Frame::parse(r#"[5,"a","FormatViolation","",{}]"#).unwrap(),
327 Frame::CallResultError { .. }
328 ));
329 assert!(matches!(
330 Frame::parse(r#"[6,"a","NotifyPeriodicEventStream",{}]"#).unwrap(),
331 Frame::Send { .. }
332 ));
333 }
334
335 #[test]
336 fn parse_errors() {
337 assert_eq!(Frame::parse("garbage").unwrap_err().reply_id(), "-1");
338 assert_eq!(Frame::parse("[2]").unwrap_err().reply_id(), "-1");
339 assert_eq!(
340 Frame::parse(r#"[2,"abc","X"]"#).unwrap_err().reply_id(),
341 "abc"
342 );
343 assert_eq!(
344 Frame::parse(r#"[2,"abc","X",[]]"#).unwrap_err().reply_id(),
345 "abc"
346 );
347 assert_eq!(
348 Frame::parse(r#"[9,"abc"]"#).unwrap_err(),
349 FrameError::UnknownMessageType {
350 id: Some("abc".into()),
351 type_id: 9
352 }
353 );
354 }
355
356 #[test]
357 fn encode() {
358 assert_eq!(
359 encode_call("1", "Heartbeat", &serde_json::json!({})).unwrap(),
360 r#"[2,"1","Heartbeat",{}]"#
361 );
362 assert_eq!(
363 encode_result("1", &serde_json::json!({"a":1})).unwrap(),
364 r#"[3,"1",{"a":1}]"#
365 );
366 let e = RpcError::new(ErrorCode::FormatViolation, "bad");
367 assert_eq!(
368 encode_error("1", &e, ErrorDialect::Ocpp16, false),
369 r#"[4,"1","FormationViolation","bad",{}]"#
370 );
371 assert_eq!(
372 encode_error("1", &e, ErrorDialect::Ocpp2, true),
373 r#"[5,"1","FormatViolation","bad",{}]"#
374 );
375 let long = RpcError::new(ErrorCode::GenericError, "x".repeat(400));
376 let s = encode_error("1", &long, ErrorDialect::Ocpp2, false);
377 assert!(s.len() < 300);
378 }
379}