1
Fork 0

Compare commits

...

2 commits

Author SHA1 Message Date
fc5d8b25fb
version: v0.2.2 2023-02-18 18:37:37 +01:00
5a367bc478
fix: Fix deserializing arrays and structs
I'm still not sure when `deserialize_any` is called over one of the
specific variants, but it seems like I do have do make `deserialize_any`
aware of all value types.

Fixes #1.
2023-02-18 18:28:44 +01:00
5 changed files with 162 additions and 9 deletions

View file

@ -6,6 +6,12 @@
== [Unreleased] == [Unreleased]
== [v0.2.2] - 2023-02-18
=== Fixed
- fix deserialization failing on arrays and objects in some cases
== [v0.2.1] - 2022-12-28 == [v0.2.1] - 2022-12-28
=== Fixed === Fixed

View file

@ -1,6 +1,6 @@
[package] [package]
name = "serde_sjson" name = "serde_sjson"
version = "0.2.1" version = "0.2.2"
edition = "2021" edition = "2021"
keywords = ["serde", "serialization", "sjson"] keywords = ["serde", "serialization", "sjson"]
description = "An SJSON serialization file format" description = "An SJSON serialization file format"

View file

@ -53,6 +53,16 @@ impl<'de> Deserializer<'de> {
Some(self.input.fragment().to_string()), Some(self.input.fragment().to_string()),
) )
} }
fn error_with_token(&self, code: ErrorCode, token: Token) -> Error {
Error::with_token(
code,
self.input.location_line(),
self.input.get_utf8_column(),
Some(self.input.fragment().to_string()),
token,
)
}
} }
pub fn from_str<'a, T>(input: &'a str) -> Result<T> pub fn from_str<'a, T>(input: &'a str) -> Result<T>
@ -79,13 +89,15 @@ impl<'de, 'a> serde::de::Deserializer<'de> for &'a mut Deserializer<'de> {
return Err(self.error(ErrorCode::ExpectedTopLevelObject)); return Err(self.error(ErrorCode::ExpectedTopLevelObject));
} }
match self.next_token()? { match self.peek_token()? {
Token::Boolean(val) => visitor.visit_bool::<Self::Error>(val), Token::Boolean(_) => self.deserialize_bool(visitor),
Token::Float(val) => visitor.visit_f64(val), Token::Float(_) => self.deserialize_f64(visitor),
Token::Integer(val) => visitor.visit_i64(val), Token::Integer(_) => self.deserialize_i64(visitor),
Token::Null => visitor.visit_unit(), Token::Null => self.deserialize_unit(visitor),
Token::String(val) => visitor.visit_str(&val), Token::String(_) => self.deserialize_str(visitor),
_ => Err(self.error(ErrorCode::ExpectedValue)), Token::ArrayStart => self.deserialize_seq(visitor),
Token::ObjectStart => self.deserialize_map(visitor),
token => Err(self.error_with_token(ErrorCode::ExpectedValue, token)),
} }
} }
@ -731,4 +743,53 @@ render_config = "core/rendering/renderer"
let actual = from_str::<()>(json); let actual = from_str::<()>(json);
assert_eq!(actual, Err(err)); assert_eq!(actual, Err(err));
} }
#[test]
fn deserialize_array() {
#[derive(Debug, Default, serde::Deserialize, PartialEq)]
struct Data {
array: Vec<String>,
}
let expected = Data {
array: vec![String::from("foo")],
};
let sjson = r#"
array = [
"foo"
]
"#;
assert_ok!(Data, expected, sjson);
}
// Regression test for #1 (https://git.sclu1034.dev/lucas/serde_sjson/issues/1)
#[test]
fn deserialize_dtmt_config() {
#[derive(Debug, Default, serde::Deserialize, PartialEq)]
struct DtmtConfig {
name: String,
#[serde(default)]
description: String,
version: Option<String>,
}
let sjson = r#"
name = "test-mod"
description = "A dummy project to test things with"
version = "0.1.0"
packages = [
"packages/test-mod"
]
"#;
let expected = DtmtConfig {
name: String::from("test-mod"),
description: String::from("A dummy project to test things with"),
version: Some(String::from("0.1.0")),
};
assert_ok!(DtmtConfig, expected, sjson);
}
} }

View file

@ -1,5 +1,7 @@
use std::fmt; use std::fmt;
use crate::parser::Token;
pub type Result<T> = std::result::Result<T, Error>; pub type Result<T> = std::result::Result<T, Error>;
#[derive(PartialEq)] #[derive(PartialEq)]
@ -13,6 +15,7 @@ struct ErrorImpl {
line: u32, line: u32,
column: usize, column: usize,
fragment: Option<String>, fragment: Option<String>,
token: Option<Token>,
} }
#[derive(PartialEq)] #[derive(PartialEq)]
@ -89,11 +92,12 @@ impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!( write!(
f, f,
"Error({:?}, line: {}, column: {}, fragment: {:?})", "Error({:?}, line: {}, column: {}, fragment: {:?}, token: {:?})",
self.inner.code.to_string(), self.inner.code.to_string(),
self.inner.line, self.inner.line,
self.inner.column, self.inner.column,
self.inner.fragment, self.inner.fragment,
self.inner.token,
) )
} }
} }
@ -108,6 +112,7 @@ impl serde::de::Error for Error {
line: 0, line: 0,
column: 0, column: 0,
fragment: None, fragment: None,
token: None,
}); });
Self { inner } Self { inner }
} }
@ -123,6 +128,7 @@ impl serde::ser::Error for Error {
line: 0, line: 0,
column: 0, column: 0,
fragment: None, fragment: None,
token: None,
}); });
Self { inner } Self { inner }
} }
@ -138,6 +144,24 @@ impl Error {
line, line,
column, column,
fragment, fragment,
token: None,
}),
}
}
pub(crate) fn with_token(
code: ErrorCode,
line: u32,
column: usize,
fragment: Option<String>,
token: Token,
) -> Self {
Self {
inner: Box::new(ErrorImpl {
code,
line,
column,
fragment,
token: Some(token),
}), }),
} }
} }

View file

@ -203,6 +203,32 @@ mod test {
}}; }};
} }
fn check_parse_result<S: AsRef<str>, T: AsRef<[Token]>>(input: S, tokens: T) {
let tokens = tokens.as_ref();
let mut remaining = Span::from(input.as_ref());
let mut i = 0;
loop {
if remaining.fragment().is_empty() {
break;
}
let (span, token) =
super::parse_next_token(remaining).expect("failed to parse next token");
assert_eq!(Some(&token), tokens.get(i));
remaining = span;
i = i + 1;
}
assert_eq!(
tokens.len(),
i,
"tokens to check against were not exhausted"
);
}
#[test] #[test]
fn parse_optional() { fn parse_optional() {
assert_ok!("\n", whitespace, "", '\n'); assert_ok!("\n", whitespace, "", '\n');
@ -265,6 +291,7 @@ mod test {
assert_ok!(r#""foo123""#, delimited_string, "", "foo123"); assert_ok!(r#""foo123""#, delimited_string, "", "foo123");
assert_ok!(r#""123foo""#, delimited_string, "", "123foo"); assert_ok!(r#""123foo""#, delimited_string, "", "123foo");
assert_ok!(r#""foo\"bar""#, delimited_string, "", "foo\\\"bar"); assert_ok!(r#""foo\"bar""#, delimited_string, "", "foo\\\"bar");
assert_ok!(r#""foo/bar""#, delimited_string, "", "foo/bar");
assert_err!("foo\"", delimited_string, ErrorKind::Char); assert_err!("foo\"", delimited_string, ErrorKind::Char);
@ -302,4 +329,39 @@ mod test {
assert_ok!("/* foo */", block_comment, "", " foo "); assert_ok!("/* foo */", block_comment, "", " foo ");
assert_ok!("/*\n\tfoo\nbar\n*/", block_comment, "", "\n\tfoo\nbar\n"); assert_ok!("/*\n\tfoo\nbar\n*/", block_comment, "", "\n\tfoo\nbar\n");
} }
// Regression test for #1 (https://git.sclu1034.dev/lucas/serde_sjson/issues/1)
#[test]
fn parse_dtmt_config() {
let sjson = r#"
name = "test-mod"
description = "A dummy project to test things with"
version = "0.1.0"
packages = [
"packages/test-mod"
]
"#;
check_parse_result(
sjson,
[
Token::String(String::from("name")),
Token::Equals,
Token::String(String::from("test-mod")),
Token::String(String::from("description")),
Token::Equals,
Token::String(String::from("A dummy project to test things with")),
Token::String(String::from("version")),
Token::Equals,
Token::String(String::from("0.1.0")),
Token::String(String::from("packages")),
Token::Equals,
Token::ArrayStart,
Token::String(String::from("packages/test-mod")),
Token::ArrayEnd,
Token::Eof,
],
);
}
} }