Compare commits
2 commits
39486e8503
...
fc5d8b25fb
Author | SHA1 | Date | |
---|---|---|---|
fc5d8b25fb | |||
5a367bc478 |
5 changed files with 162 additions and 9 deletions
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
75
src/de.rs
75
src/de.rs
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
26
src/error.rs
26
src/error.rs
|
@ -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),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue