diff --git a/hjson/Cargo.toml b/hjson/Cargo.toml index ffca14e..abb7dc8 100644 --- a/hjson/Cargo.toml +++ b/hjson/Cargo.toml @@ -16,6 +16,6 @@ default = ["preserve_order"] [dependencies] serde = "1.0" num-traits = "0.2" -regex = "1.0" +regex = "1.10" lazy_static = "1" linked-hash-map = { version = "0.5", optional = true } diff --git a/hjson/src/de.rs b/hjson/src/de.rs index 14898bf..2c8074b 100644 --- a/hjson/src/de.rs +++ b/hjson/src/de.rs @@ -276,8 +276,20 @@ where } } if is_eol { + let pos = self.rdr.pos(); // remove any whitespace at the end (ignored in quoteless strings) - return visitor.visit_str(str::from_utf8(&self.str_buf).unwrap().trim()); + return visitor.visit_str( + str::from_utf8(&self.str_buf) + .map_err(|_| { + let reader_error = super::Error::Syntax( + super::ErrorCode::EOFWhileParsingString, + pos.0, + pos.1, + ); + reader_error + })? + .trim(), + ); } } self.str_buf.push(ch); @@ -345,7 +357,9 @@ where // we are at ''' +1 - get indent let (_, col) = self.rdr.pos(); - let indent = col - 4; + + // Fallback machanism if col is improper + let indent = if col >= 4 { col - 4 } else { 0 }; // skip white/to (newline) while self.ml_skip_white()? {} @@ -813,5 +827,10 @@ pub fn from_str(s: &str) -> Result where T: de::DeserializeOwned, { - from_slice(s.as_bytes()) + if s.chars().last().is_some_and(|x| x.is_whitespace()) { + from_slice(s.as_bytes()) + } else { + let s = format!("{s}\n"); + from_slice(s.as_bytes()) + } } diff --git a/hjson/src/util.rs b/hjson/src/util.rs index 7ece8f8..50e942a 100644 --- a/hjson/src/util.rs +++ b/hjson/src/util.rs @@ -71,7 +71,11 @@ where } pub fn eat_char(&mut self) -> u8 { - self.ch.remove(0) + if !self.ch.is_empty() { + self.ch.remove(0) + // Fallback mechanism for consuming a character. + // Sets ch to NULL termination + } else { 0 } } pub fn uneat_char(&mut self, ch: u8) { @@ -207,12 +211,21 @@ impl> ParseNumber { } if is_float { - Ok(Number::F64(res.parse::().unwrap())) + Ok(Number::F64(res.parse::().map_err(|_| { + let pos = self.rdr.pos(); + Error::Syntax(ErrorCode::InvalidNumber, pos.0, pos.1) + })?)) } else { if res.starts_with("-") { - Ok(Number::I64(res.parse::().unwrap())) + Ok(Number::I64(res.parse::().map_err(|_| { + let pos = self.rdr.pos(); + Error::Syntax(ErrorCode::InvalidNumber, pos.0, pos.1) + })?)) } else { - Ok(Number::U64(res.parse::().unwrap())) + Ok(Number::U64(res.parse::().map_err(|_| { + let pos = self.rdr.pos(); + Error::Syntax(ErrorCode::InvalidNumber, pos.0, pos.1) + })?)) } } } diff --git a/hjson/src/value.rs b/hjson/src/value.rs index 7788c54..da38c87 100644 --- a/hjson/src/value.rs +++ b/hjson/src/value.rs @@ -1153,7 +1153,7 @@ impl<'de, 'a> de::MapAccess<'de> for MapDeserializer { /// ```rust /// use serde_hjson::to_value; /// let val = to_value("foo"); -/// assert_eq!(val.as_str(), Some("foo")) +/// assert_eq!(val.unwrap().as_str(), Some("foo")) /// ``` pub fn to_value(value: &T) -> Result where diff --git a/hjson_tests/assets/simplenumber_result.hjson b/hjson_tests/assets/simplenumber_result.hjson new file mode 100644 index 0000000..190a180 --- /dev/null +++ b/hjson_tests/assets/simplenumber_result.hjson @@ -0,0 +1 @@ +123 diff --git a/hjson_tests/assets/simplenumber_result.json b/hjson_tests/assets/simplenumber_result.json new file mode 100644 index 0000000..190a180 --- /dev/null +++ b/hjson_tests/assets/simplenumber_result.json @@ -0,0 +1 @@ +123 diff --git a/hjson_tests/assets/simplenumber_test.hjson b/hjson_tests/assets/simplenumber_test.hjson new file mode 100644 index 0000000..190a180 --- /dev/null +++ b/hjson_tests/assets/simplenumber_test.hjson @@ -0,0 +1 @@ +123 diff --git a/hjson_tests/tests/test_hjson.rs b/hjson_tests/tests/test_hjson.rs index 6dc1d5a..2c90519 100644 --- a/hjson_tests/tests/test_hjson.rs +++ b/hjson_tests/tests/test_hjson.rs @@ -3,11 +3,15 @@ extern crate serde_hjson; extern crate serde_json; use regex::Regex; -use serde_hjson::Value; use std::borrow::Cow; use std::fs; use std::io; use std::path::Path; +use serde_hjson::{Map, Value}; +use serde_hjson::error::Result; + +pub const TRIM_ENDLINE: bool = true; + fn get_test_content(name: &str) -> io::Result { let mut p = format!("./assets/{}_test.hjson", name); @@ -24,19 +28,22 @@ fn get_result_content(name: &str) -> io::Result<(String, String)> { } macro_rules! run_test { - // {{ is a workaround for rust stable ($v: ident, $list: expr, $fix: expr) => {{ + run_test!($v, $list, $fix, false); + }}; + // {{ is a workaround for rust stable + ($v: ident, $list: expr, $fix: expr, $trim_endline: expr) => {{ let name = stringify!($v); $list.push(format!("{}_test", name)); println!("- running {}", name); let should_fail = name.starts_with("fail"); - let test_content = get_test_content(name).unwrap(); + let test_content = get_test_content(name).expect("Could not read test content"); let data: serde_hjson::Result = serde_hjson::from_str(&test_content); assert!(should_fail == data.is_err()); if !should_fail { let udata = data.unwrap(); - let (rjson, rhjson) = get_result_content(name).unwrap(); + let (rjson, rhjson) = get_result_content(name).expect("Could not read result content"); let actual_hjson = serde_hjson::to_string(&udata).unwrap(); let actual_json = serde_json::to_string_pretty(&udata).unwrap(); let actual_json = $fix(&actual_json); @@ -52,7 +59,10 @@ macro_rules! run_test { name, rjson, actual_json ); } - assert!(rhjson == actual_hjson && rjson == actual_json); + if $trim_endline { assert!(rhjson.trim_end() == actual_hjson && rjson.trim_end() == actual_json); } + else { + assert!(rhjson == actual_hjson && rjson == actual_json); + } } }}; } @@ -155,6 +165,7 @@ fn test_hjson() { run_test!(stringify1, done, std_fix); run_test!(strings, done, std_fix); run_test!(trail, done, std_fix); + run_test!(simplenumber, done, std_fix, TRIM_ENDLINE); // check if we include all assets let paths = fs::read_dir("./assets/").unwrap(); @@ -176,3 +187,41 @@ fn test_hjson() { assert!(false); } } + +#[test] +pub fn parse_int_error() { + let data: Vec = vec![47, 97, 47, 65, 58, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 0, 54, 35, 54, 54, 54, 54, 54, 54, 54, 44, 35, 58, 45, 85, 85, 85, 35, 116, 45, 35, 35, 58, 47]; + + let sample: Result> = serde_hjson::from_slice(&data); + assert!(sample.is_err()) +} + +#[test] +pub fn removal_index() { + let data: Vec = vec![47, 42, 44, 45]; + + let sample: Result> = serde_hjson::from_slice(&data); + assert!(sample.is_err()) +} + +#[test] +pub fn subtract_overflow() { + let data: Vec = vec![39, 39, 39]; + + let sample: Result> = serde_hjson::from_slice(&data); + assert!(sample.is_err()) +} + +#[test] +pub fn invalid_utf8() { + let data: Vec = vec![155]; + + let sample: Result> = serde_hjson::from_slice(&data); + assert!(sample.is_err()) +} + +#[test] +pub fn integer_type() { + let json: Value = serde_hjson::from_str("123").unwrap(); + assert!(json.is_number()) +}