From 6a16e68900b84ffe982bee38a2eb264158ae7b6b Mon Sep 17 00:00:00 2001 From: Fabrice Reix Date: Thu, 5 Nov 2020 20:25:47 +0100 Subject: [PATCH] Add your own json serializer --- packages/hurlfmt/src/format/serialize_json.rs | 134 ++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 packages/hurlfmt/src/format/serialize_json.rs diff --git a/packages/hurlfmt/src/format/serialize_json.rs b/packages/hurlfmt/src/format/serialize_json.rs new file mode 100644 index 000000000..5aaf30b9e --- /dev/null +++ b/packages/hurlfmt/src/format/serialize_json.rs @@ -0,0 +1,134 @@ +/* + * hurl (https://hurl.dev) + * Copyright (C) 2020 Orange + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** + * Serde json son can not be easily used for serialization here because of the orphan rule. + * It seems easier just to reimplement it from scratch (around 50 lines of code) + */ + +#[allow(dead_code)] +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum JValue { + Number(String), + String(String), + Boolean(bool), + List(Vec), + Object(Vec<(String, JValue)>), + Null, +} + +impl JValue { + pub fn format(self) -> String { + match self { + JValue::Null => "null".to_string(), + JValue::Number(n) => n, + JValue::String(s) => format!("\"{}\"", s.chars().map(format_char).collect::()), + JValue::Boolean(b) => b.to_string(), + JValue::List(elem) => { + let s = elem + .iter() + .map(|e| e.clone().format()) + .collect::>() + .join(","); + format!("[{}]", s) + } + JValue::Object(key_values) => { + let s = key_values + .iter() + .map(|(k, v)| format!("\"{}\":{}", k, v.clone().format())) + .collect::>() + .join(","); + format!("{{{}}}", s) + } + } + } +} + +fn format_char(c: char) -> String { + if c == '"' { + "\\\"".to_string() + } else if c == '\\' { + "\\\\".to_string() + } else if c == '\x08' { + "\\b".to_string() + } else if c == '\x0c' { + "\\f".to_string() + } else if c == '\n' { + "\\n".to_string() + } else if c == '\r' { + "\\r".to_string() + } else if c == '\t' { + "\\t".to_string() + } else if c.is_control() { + format!("\\u{:04x}", c as u32) + } else { + c.to_string() + } +} + +#[cfg(test)] +pub mod tests { + use super::*; + + #[test] + pub fn test_format_char() { + assert_eq!(format_char('a'), "a"); + assert_eq!(format_char('"'), "\\\""); // \" + assert_eq!(format_char('\n'), "\\n"); + assert_eq!(format_char('\x07'), "\\u0007") + } + + #[test] + pub fn format_scalars() { + assert_eq!(JValue::Null {}.format(), "null"); + assert_eq!(JValue::Number("1.0".to_string()).format(), "1.0"); + assert_eq!(JValue::String("hello".to_string()).format(), "\"hello\""); + assert_eq!(JValue::Boolean(true).format(), "true"); + } + + #[test] + pub fn format_string() { + assert_eq!(JValue::String("hello".to_string()).format(), "\"hello\""); + assert_eq!(JValue::String("\"".to_string()).format(), r#""\"""#); + } + + #[test] + pub fn format_list() { + assert_eq!( + JValue::List(vec![ + JValue::Number("1".to_string()), + JValue::Number("2".to_string()), + JValue::Number("3".to_string()) + ]) + .format(), + "[1,2,3]" + ); + } + + #[test] + pub fn format_object() { + assert_eq!( + JValue::Object(vec![ + ("name".to_string(), JValue::String("Bob".to_string())), + ("age".to_string(), JValue::Number("20".to_string())), + ]) + .format(), + r#"{"name":"Bob","age":20}"# + ); + } +}