Compare commits
No commits in common. "master" and "v0.1.0" have entirely different histories.
14 changed files with 340 additions and 1669 deletions
14
CHANGELOG.adoc
Normal file
14
CHANGELOG.adoc
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
= Changelog
|
||||||
|
:toc:
|
||||||
|
:toclevels: 1
|
||||||
|
:idprefix:
|
||||||
|
:idseparator: -
|
||||||
|
|
||||||
|
== [Unreleased]
|
||||||
|
|
||||||
|
== [v0.1.0] - 2022-11-18
|
||||||
|
|
||||||
|
=== Added
|
||||||
|
|
||||||
|
* initial release
|
||||||
|
* serialization
|
69
CHANGELOG.md
69
CHANGELOG.md
|
@ -1,69 +0,0 @@
|
||||||
# Changelog
|
|
||||||
|
|
||||||
<!-- next-header -->
|
|
||||||
|
|
||||||
## [Unreleased] - ReleaseDate
|
|
||||||
|
|
||||||
## [1.2.0] - 2024-03-21
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- publishing to [crates.io](https://crates.io)
|
|
||||||
|
|
||||||
## [v1.1.0] - 2024-03-21
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- implement serializing into generic `io::Write`
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- fix parsing CRLF
|
|
||||||
|
|
||||||
## [v1.0.0] - 2023-03-10
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- implement literal strings
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- fix serializing strings containing `:`
|
|
||||||
- fix serializing certain escaped characters
|
|
||||||
|
|
||||||
## [v0.2.4] - 2023-03-01
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- fix incorrect parsing of unquoted strings
|
|
||||||
|
|
||||||
## [v0.2.3] - 2023-02-24
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- support backslashes in delimited strings
|
|
||||||
|
|
||||||
## [v0.2.2] - 2023-02-18
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- fix deserialization failing on arrays and objects in some cases
|
|
||||||
|
|
||||||
## [v0.2.1] - 2022-12-28
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- fix serializing Unicode
|
|
||||||
|
|
||||||
## [v0.2.0] - 2022-11-25
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
* parsing & deserialization
|
|
||||||
|
|
||||||
## [v0.1.0] - 2022-11-18
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
* initial release
|
|
||||||
* serialization
|
|
24
Cargo.toml
24
Cargo.toml
|
@ -1,27 +1,13 @@
|
||||||
[package]
|
[package]
|
||||||
name = "serde_sjson"
|
name = "serde_sjson"
|
||||||
version = "1.2.0"
|
version = "0.1.0"
|
||||||
authors = ["Lucas Schwiderski"]
|
|
||||||
categories = ["encoding", "parser-implementations"]
|
|
||||||
description = "An SJSON serialization file format"
|
|
||||||
documentation = "https://docs.rs/serde_sjson"
|
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
keywords = ["serde", "serialization", "sjson"]
|
keywords = ["serde", "serialization", "sjson"]
|
||||||
license-file = "LICENSE"
|
description = "An SJSON serialization file format"
|
||||||
repository = "https://github.com/sclu1034/serde_sjson"
|
categories = ["encoding", "parser-implementations"]
|
||||||
exclude = [
|
|
||||||
".github/",
|
|
||||||
".ci/",
|
|
||||||
"Justfile"
|
|
||||||
]
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nom = "7"
|
serde = { version = "1.0.147", default-features = false }
|
||||||
nom_locate = "4.1"
|
|
||||||
serde = { version = "1.0", default-features = false }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
serde = { version = "1.0.194", features = ["derive"] }
|
serde = { version = "1.0.147", features = ["derive"] }
|
||||||
|
|
||||||
[badges]
|
|
||||||
maintenance = { status = "passively-maintained" }
|
|
||||||
|
|
14
Justfile
14
Justfile
|
@ -1,26 +1,12 @@
|
||||||
project := "serde_sjson"
|
project := "serde_sjson"
|
||||||
default := "run"
|
default := "run"
|
||||||
|
|
||||||
build *ARGS:
|
|
||||||
cargo build {{ARGS}}
|
|
||||||
cargo readme > README.md
|
|
||||||
|
|
||||||
run *ARGS:
|
run *ARGS:
|
||||||
cargo run -- {{ARGS}}
|
cargo run -- {{ARGS}}
|
||||||
|
|
||||||
test *ARGS:
|
test *ARGS:
|
||||||
cargo test {{ARGS}}
|
cargo test {{ARGS}}
|
||||||
|
|
||||||
doc:
|
|
||||||
cargo doc --no-deps
|
|
||||||
cargo readme > README.md
|
|
||||||
|
|
||||||
serve-doc port='8000': doc
|
|
||||||
python3 -m http.server {{port}} --directory target/doc
|
|
||||||
|
|
||||||
release version execute='':
|
|
||||||
cargo release --sign --allow-branch master {{ if execute != "" { '-x' } else { '' } }} {{version}}
|
|
||||||
|
|
||||||
coverage *ARGS:
|
coverage *ARGS:
|
||||||
RUSTFLAGS="-C instrument-coverage" cargo test --tests {{ARGS}} || true
|
RUSTFLAGS="-C instrument-coverage" cargo test --tests {{ARGS}} || true
|
||||||
cargo profdata -- merge -sparse default*.profraw -o {{project}}.profdata
|
cargo profdata -- merge -sparse default*.profraw -o {{project}}.profdata
|
||||||
|
|
13
README.adoc
Normal file
13
README.adoc
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
= Serde SJSON
|
||||||
|
:idprefix:
|
||||||
|
:idseparator:
|
||||||
|
:toc: macro
|
||||||
|
:toclevels: 1
|
||||||
|
:!toc-title:
|
||||||
|
:caution-caption: :fire:
|
||||||
|
:important-caption: :exclamtion:
|
||||||
|
:note-caption: :paperclip:
|
||||||
|
:tip-caption: :bulb:
|
||||||
|
:warning-caption: :warning:
|
||||||
|
|
||||||
|
A __ser__ialization/__de__serialization library for __Simplified JSON__, specifically, the Bitsquid/Stingray flavor.
|
69
README.md
69
README.md
|
@ -1,69 +0,0 @@
|
||||||
# serde_sjson
|
|
||||||
|
|
||||||
A **ser**ialization/**de**serialization library for Simplified JSON,
|
|
||||||
the Bitsquid/Stingray flavor of JSON.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
### Serializing
|
|
||||||
|
|
||||||
```rust
|
|
||||||
use serde::Serialize;
|
|
||||||
use serde_sjson::Result;
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
|
||||||
struct Person {
|
|
||||||
name: String,
|
|
||||||
age: u8,
|
|
||||||
friends: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
|
||||||
let data = Person {
|
|
||||||
name: String::from("Marc"),
|
|
||||||
age: 21,
|
|
||||||
friends: vec![String::from("Jessica"), String::from("Paul")],
|
|
||||||
};
|
|
||||||
|
|
||||||
let s = serde_sjson::to_string(&data)?;
|
|
||||||
|
|
||||||
println!("{}", s);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Deserializing
|
|
||||||
|
|
||||||
```rust
|
|
||||||
use serde::Deserialize;
|
|
||||||
use serde_sjson::Result;
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct Person {
|
|
||||||
name: String,
|
|
||||||
age: u8,
|
|
||||||
friends: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
|
||||||
let sjson = r#"
|
|
||||||
name = Marc
|
|
||||||
age = 21
|
|
||||||
friends = [
|
|
||||||
Jessica
|
|
||||||
Paul
|
|
||||||
]"#;
|
|
||||||
|
|
||||||
let data: Person = serde_sjson::from_str(sjson)?;
|
|
||||||
|
|
||||||
println!(
|
|
||||||
"{} is {} years old and has {} friends.",
|
|
||||||
data.name,
|
|
||||||
data.age,
|
|
||||||
data.friends.len()
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
```
|
|
|
@ -1,3 +0,0 @@
|
||||||
# {{crate}}
|
|
||||||
|
|
||||||
{{readme}}
|
|
|
@ -1,5 +0,0 @@
|
||||||
pre-release-replacements = [
|
|
||||||
{file="CHANGELOG.md", search="Unreleased", replace="{{version}}"},
|
|
||||||
{file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}"},
|
|
||||||
{file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n\n## [Unreleased] - ReleaseDate", exactly=1},
|
|
||||||
]
|
|
717
src/de.rs
717
src/de.rs
|
@ -1,798 +1,233 @@
|
||||||
use nom::IResult;
|
pub struct Deserializer {}
|
||||||
use serde::de::{EnumAccess, IntoDeserializer, VariantAccess};
|
|
||||||
use serde::Deserialize;
|
|
||||||
|
|
||||||
use crate::error::{Error, ErrorCode, Result};
|
impl serde::Deserializer for Deserializer {
|
||||||
use crate::parser::*;
|
type Error;
|
||||||
|
|
||||||
/// A container for deserializing Rust values from SJSON.
|
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||||
pub struct Deserializer<'de> {
|
|
||||||
input: Span<'de>,
|
|
||||||
is_top_level: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> Deserializer<'de> {
|
|
||||||
#![allow(clippy::should_implement_trait)]
|
|
||||||
pub(crate) fn from_str(input: &'de str) -> Self {
|
|
||||||
Self {
|
|
||||||
input: Span::from(input),
|
|
||||||
is_top_level: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse(&mut self, f: &dyn Fn(Span) -> IResult<Span, Token>) -> Result<Token> {
|
|
||||||
f(self.input)
|
|
||||||
.map(|(span, token)| {
|
|
||||||
self.input = span;
|
|
||||||
token
|
|
||||||
})
|
|
||||||
.map_err(|err| self.error(ErrorCode::Message(err.to_string())))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next_token(&mut self) -> Result<Token> {
|
|
||||||
match parse_next_token(self.input) {
|
|
||||||
Ok((span, token)) => {
|
|
||||||
self.input = span;
|
|
||||||
Ok(token)
|
|
||||||
}
|
|
||||||
Err(err) => Err(self.error(ErrorCode::Message(err.to_string()))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn peek_token(&mut self) -> Result<Token> {
|
|
||||||
match parse_next_token(self.input) {
|
|
||||||
Ok((_, token)) => Ok(token),
|
|
||||||
Err(err) => Err(self.error(ErrorCode::Message(err.to_string()))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn error(&self, code: ErrorCode) -> Error {
|
|
||||||
Error::new(
|
|
||||||
code,
|
|
||||||
self.input.location_line(),
|
|
||||||
self.input.get_utf8_column(),
|
|
||||||
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,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deserializes an SJSON string to a Rust value.
|
|
||||||
#[inline]
|
|
||||||
pub fn from_str<'a, T>(input: &'a str) -> Result<T>
|
|
||||||
where
|
|
||||||
T: Deserialize<'a>,
|
|
||||||
{
|
|
||||||
let mut de = Deserializer::from_str(input);
|
|
||||||
let t = T::deserialize(&mut de)?;
|
|
||||||
if de.input.is_empty() || parse_trailing_characters(de.input).is_ok() {
|
|
||||||
Ok(t)
|
|
||||||
} else {
|
|
||||||
Err(de.error(ErrorCode::TrailingCharacters))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de, 'a> serde::de::Deserializer<'de> for &'a mut Deserializer<'de> {
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
|
|
||||||
where
|
where
|
||||||
V: serde::de::Visitor<'de>,
|
V: serde::de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
if self.is_top_level {
|
todo!()
|
||||||
return Err(self.error(ErrorCode::ExpectedTopLevelObject));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.peek_token()? {
|
fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||||
Token::Boolean(_) => self.deserialize_bool(visitor),
|
|
||||||
Token::Float(_) => self.deserialize_f64(visitor),
|
|
||||||
Token::Integer(_) => self.deserialize_i64(visitor),
|
|
||||||
Token::Null => self.deserialize_unit(visitor),
|
|
||||||
Token::String(_) => self.deserialize_str(visitor),
|
|
||||||
Token::ArrayStart => self.deserialize_seq(visitor),
|
|
||||||
Token::ObjectStart => self.deserialize_map(visitor),
|
|
||||||
token => Err(self.error_with_token(ErrorCode::ExpectedValue, token)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value>
|
|
||||||
where
|
where
|
||||||
V: serde::de::Visitor<'de>,
|
V: serde::de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
if self.is_top_level {
|
todo!()
|
||||||
return Err(self.error(ErrorCode::ExpectedTopLevelObject));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(Token::Boolean(val)) = self.parse(&parse_bool) {
|
fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||||
visitor.visit_bool(val)
|
|
||||||
} else {
|
|
||||||
Err(self.error(ErrorCode::ExpectedBoolean))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value>
|
|
||||||
where
|
where
|
||||||
V: serde::de::Visitor<'de>,
|
V: serde::de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
self.deserialize_i64(visitor)
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value>
|
fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||||
where
|
where
|
||||||
V: serde::de::Visitor<'de>,
|
V: serde::de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
self.deserialize_i64(visitor)
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value>
|
fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||||
where
|
where
|
||||||
V: serde::de::Visitor<'de>,
|
V: serde::de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
self.deserialize_i64(visitor)
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value>
|
fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||||
where
|
where
|
||||||
V: serde::de::Visitor<'de>,
|
V: serde::de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
if self.is_top_level {
|
todo!()
|
||||||
return Err(self.error(ErrorCode::ExpectedTopLevelObject));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(Token::Integer(val)) = self.parse(&parse_integer) {
|
fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||||
visitor.visit_i64(val)
|
|
||||||
} else {
|
|
||||||
Err(self.error(ErrorCode::ExpectedInteger))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value>
|
|
||||||
where
|
where
|
||||||
V: serde::de::Visitor<'de>,
|
V: serde::de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
self.deserialize_i64(visitor)
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value>
|
fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||||
where
|
where
|
||||||
V: serde::de::Visitor<'de>,
|
V: serde::de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
self.deserialize_i64(visitor)
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value>
|
fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||||
where
|
where
|
||||||
V: serde::de::Visitor<'de>,
|
V: serde::de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
self.deserialize_i64(visitor)
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value>
|
fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||||
where
|
where
|
||||||
V: serde::de::Visitor<'de>,
|
V: serde::de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
self.deserialize_i64(visitor)
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value>
|
fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||||
where
|
where
|
||||||
V: serde::de::Visitor<'de>,
|
V: serde::de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
self.deserialize_f64(visitor)
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value>
|
fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||||
where
|
where
|
||||||
V: serde::de::Visitor<'de>,
|
V: serde::de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
if self.is_top_level {
|
todo!()
|
||||||
return Err(self.error(ErrorCode::ExpectedTopLevelObject));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(Token::Float(val)) = self.parse(&parse_float) {
|
fn deserialize_char<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||||
visitor.visit_f64(val)
|
|
||||||
} else {
|
|
||||||
Err(self.error(ErrorCode::ExpectedFloat))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize_char<V>(self, visitor: V) -> Result<V::Value>
|
|
||||||
where
|
where
|
||||||
V: serde::de::Visitor<'de>,
|
V: serde::de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
self.deserialize_str(visitor)
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_str<V>(self, visitor: V) -> Result<V::Value>
|
fn deserialize_str<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||||
where
|
where
|
||||||
V: serde::de::Visitor<'de>,
|
V: serde::de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
if self.is_top_level {
|
todo!()
|
||||||
return Err(self.error(ErrorCode::ExpectedTopLevelObject));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(Token::String(val)) = self.parse(&parse_string) {
|
fn deserialize_string<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||||
visitor.visit_str(&val)
|
|
||||||
} else {
|
|
||||||
Err(self.error(ErrorCode::ExpectedString))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize_string<V>(self, visitor: V) -> Result<V::Value>
|
|
||||||
where
|
where
|
||||||
V: serde::de::Visitor<'de>,
|
V: serde::de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
self.deserialize_str(visitor)
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_bytes<V>(self, _visitor: V) -> Result<V::Value>
|
fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||||
where
|
where
|
||||||
V: serde::de::Visitor<'de>,
|
V: serde::de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
unimplemented!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_byte_buf<V>(self, _visitor: V) -> Result<V::Value>
|
fn deserialize_byte_buf<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||||
where
|
where
|
||||||
V: serde::de::Visitor<'de>,
|
V: serde::de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
unimplemented!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value>
|
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||||
where
|
where
|
||||||
V: serde::de::Visitor<'de>,
|
V: serde::de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
if self.is_top_level {
|
todo!()
|
||||||
return Err(self.error(ErrorCode::ExpectedTopLevelObject));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.peek_token()? == Token::Null {
|
fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||||
let _ = self.next_token()?;
|
|
||||||
visitor.visit_none()
|
|
||||||
} else {
|
|
||||||
visitor.visit_some(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value>
|
|
||||||
where
|
where
|
||||||
V: serde::de::Visitor<'de>,
|
V: serde::de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
if self.is_top_level {
|
todo!()
|
||||||
return Err(self.error(ErrorCode::ExpectedTopLevelObject));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(Token::Null) = self.parse(&parse_null) {
|
fn deserialize_unit_struct<V>(
|
||||||
visitor.visit_unit()
|
self,
|
||||||
} else {
|
name: &'static str,
|
||||||
Err(self.error(ErrorCode::ExpectedNull))
|
visitor: V,
|
||||||
}
|
) -> Result<V::Value, Self::Error>
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize_unit_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value>
|
|
||||||
where
|
where
|
||||||
V: serde::de::Visitor<'de>,
|
V: serde::de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
self.deserialize_unit(visitor)
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_newtype_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value>
|
fn deserialize_newtype_struct<V>(
|
||||||
|
self,
|
||||||
|
name: &'static str,
|
||||||
|
visitor: V,
|
||||||
|
) -> Result<V::Value, Self::Error>
|
||||||
where
|
where
|
||||||
V: serde::de::Visitor<'de>,
|
V: serde::de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
visitor.visit_newtype_struct(self)
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value>
|
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||||
where
|
where
|
||||||
V: serde::de::Visitor<'de>,
|
V: serde::de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
if self.is_top_level {
|
todo!()
|
||||||
return Err(self.error(ErrorCode::ExpectedTopLevelObject));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.next_token()? != Token::ArrayStart {
|
fn deserialize_tuple<V>(self, len: usize, visitor: V) -> Result<V::Value, Self::Error>
|
||||||
return Err(self.error(ErrorCode::ExpectedArray));
|
|
||||||
}
|
|
||||||
|
|
||||||
let value = visitor.visit_seq(Separated::new(self))?;
|
|
||||||
|
|
||||||
if self.next_token()? == Token::ArrayEnd {
|
|
||||||
Ok(value)
|
|
||||||
} else {
|
|
||||||
Err(self.error(ErrorCode::ExpectedArrayEnd))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize_tuple<V>(self, _len: usize, visitor: V) -> Result<V::Value>
|
|
||||||
where
|
where
|
||||||
V: serde::de::Visitor<'de>,
|
V: serde::de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
self.deserialize_seq(visitor)
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_tuple_struct<V>(
|
fn deserialize_tuple_struct<V>(
|
||||||
self,
|
self,
|
||||||
_name: &'static str,
|
name: &'static str,
|
||||||
_len: usize,
|
len: usize,
|
||||||
visitor: V,
|
visitor: V,
|
||||||
) -> Result<V::Value>
|
) -> Result<V::Value, Self::Error>
|
||||||
where
|
where
|
||||||
V: serde::de::Visitor<'de>,
|
V: serde::de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
self.deserialize_seq(visitor)
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_map<V>(self, visitor: V) -> Result<V::Value>
|
fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||||
where
|
where
|
||||||
V: serde::de::Visitor<'de>,
|
V: serde::de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
if self.is_top_level {
|
todo!()
|
||||||
self.is_top_level = false;
|
|
||||||
|
|
||||||
visitor.visit_map(Separated::new(self))
|
|
||||||
} else {
|
|
||||||
if self.next_token()? != Token::ObjectStart {
|
|
||||||
return Err(self.error(ErrorCode::ExpectedMap));
|
|
||||||
}
|
|
||||||
|
|
||||||
let value = visitor.visit_map(Separated::new(self))?;
|
|
||||||
if self.next_token()? == Token::ObjectEnd {
|
|
||||||
Ok(value)
|
|
||||||
} else {
|
|
||||||
Err(self.error(ErrorCode::ExpectedMapEnd))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_struct<V>(
|
fn deserialize_struct<V>(
|
||||||
self,
|
self,
|
||||||
_name: &'static str,
|
name: &'static str,
|
||||||
_fields: &'static [&'static str],
|
fields: &'static [&'static str],
|
||||||
visitor: V,
|
visitor: V,
|
||||||
) -> Result<V::Value>
|
) -> Result<V::Value, Self::Error>
|
||||||
where
|
where
|
||||||
V: serde::de::Visitor<'de>,
|
V: serde::de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
self.deserialize_map(visitor)
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_enum<V>(
|
fn deserialize_enum<V>(
|
||||||
self,
|
self,
|
||||||
_name: &'static str,
|
name: &'static str,
|
||||||
_variants: &'static [&'static str],
|
variants: &'static [&'static str],
|
||||||
visitor: V,
|
visitor: V,
|
||||||
) -> Result<V::Value>
|
) -> Result<V::Value, Self::Error>
|
||||||
where
|
where
|
||||||
V: serde::de::Visitor<'de>,
|
V: serde::de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
match self.next_token()? {
|
todo!()
|
||||||
Token::String(val) => visitor.visit_enum(val.into_deserializer()),
|
|
||||||
Token::ObjectStart => {
|
|
||||||
let value = visitor.visit_enum(Enum::new(self))?;
|
|
||||||
|
|
||||||
if self.next_token()? == Token::ObjectEnd {
|
|
||||||
Ok(value)
|
|
||||||
} else {
|
|
||||||
Err(self.error(ErrorCode::ExpectedMapEnd))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => Err(self.error(ErrorCode::ExpectedEnum)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value>
|
fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||||
where
|
where
|
||||||
V: serde::de::Visitor<'de>,
|
V: serde::de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
if let Ok(Token::String(val)) = self.parse(&parse_identifier) {
|
todo!()
|
||||||
visitor.visit_str(&val)
|
|
||||||
} else {
|
|
||||||
Err(self.error(ErrorCode::ExpectedString))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value>
|
fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||||
where
|
where
|
||||||
V: serde::de::Visitor<'de>,
|
V: serde::de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
self.deserialize_any(visitor)
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Separated<'a, 'de: 'a> {
|
pub fn from_str<T>() -> crate::Result<T> {}
|
||||||
de: &'a mut Deserializer<'de>,
|
|
||||||
first: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'de: 'a> Separated<'a, 'de> {
|
|
||||||
fn new(de: &'a mut Deserializer<'de>) -> Self {
|
|
||||||
Self { de, first: true }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de, 'a> serde::de::SeqAccess<'de> for Separated<'a, 'de> {
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
|
|
||||||
where
|
|
||||||
T: serde::de::DeserializeSeed<'de>,
|
|
||||||
{
|
|
||||||
if self.de.peek_token()? == Token::ArrayEnd {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
if !self.first && self.de.parse(&parse_separator)? != Token::Separator {
|
|
||||||
return Err(self.de.error(ErrorCode::ExpectedArraySeparator));
|
|
||||||
}
|
|
||||||
|
|
||||||
self.first = false;
|
|
||||||
|
|
||||||
// TODO: Shouldn't I check that this is a valid value?
|
|
||||||
seed.deserialize(&mut *self.de).map(Some)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de, 'a> serde::de::MapAccess<'de> for Separated<'a, 'de> {
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>>
|
|
||||||
where
|
|
||||||
K: serde::de::DeserializeSeed<'de>,
|
|
||||||
{
|
|
||||||
if matches!(self.de.peek_token()?, Token::ObjectEnd | Token::Eof) {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
if !self.first && self.de.parse(&parse_separator)? != Token::Separator {
|
|
||||||
return Err(self.de.error(ErrorCode::ExpectedMapSeparator));
|
|
||||||
}
|
|
||||||
|
|
||||||
self.first = false;
|
|
||||||
|
|
||||||
// TODO: Shouldn't I check that this is a valid identifier?
|
|
||||||
seed.deserialize(&mut *self.de).map(Some)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value>
|
|
||||||
where
|
|
||||||
V: serde::de::DeserializeSeed<'de>,
|
|
||||||
{
|
|
||||||
if self.de.next_token()? != Token::Equals {
|
|
||||||
return Err(self.de.error(ErrorCode::ExpectedMapEquals));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Shouldn't I check that this is a valid value?
|
|
||||||
seed.deserialize(&mut *self.de)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Enum<'a, 'de: 'a> {
|
|
||||||
de: &'a mut Deserializer<'de>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'de> Enum<'a, 'de> {
|
|
||||||
fn new(de: &'a mut Deserializer<'de>) -> Self {
|
|
||||||
Self { de }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de, 'a> EnumAccess<'de> for Enum<'a, 'de> {
|
|
||||||
type Error = Error;
|
|
||||||
type Variant = Self;
|
|
||||||
|
|
||||||
fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant)>
|
|
||||||
where
|
|
||||||
V: serde::de::DeserializeSeed<'de>,
|
|
||||||
{
|
|
||||||
let val = seed.deserialize(&mut *self.de)?;
|
|
||||||
|
|
||||||
if self.de.next_token()? == Token::Equals {
|
|
||||||
Ok((val, self))
|
|
||||||
} else {
|
|
||||||
Err(self.de.error(ErrorCode::ExpectedMapEquals))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de, 'a> VariantAccess<'de> for Enum<'a, 'de> {
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn unit_variant(self) -> Result<()> {
|
|
||||||
Err(self.de.error(ErrorCode::ExpectedString))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value>
|
|
||||||
where
|
|
||||||
T: serde::de::DeserializeSeed<'de>,
|
|
||||||
{
|
|
||||||
seed.deserialize(self.de)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tuple_variant<V>(self, _len: usize, visitor: V) -> Result<V::Value>
|
|
||||||
where
|
|
||||||
V: serde::de::Visitor<'de>,
|
|
||||||
{
|
|
||||||
serde::Deserializer::deserialize_seq(self.de, visitor)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn struct_variant<V>(self, _fields: &'static [&'static str], visitor: V) -> Result<V::Value>
|
|
||||||
where
|
|
||||||
V: serde::de::Visitor<'de>,
|
|
||||||
{
|
|
||||||
serde::Deserializer::deserialize_map(self.de, visitor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use crate::error::{Error, ErrorCode};
|
|
||||||
use crate::from_str;
|
|
||||||
|
|
||||||
macro_rules! assert_value_ok {
|
|
||||||
($type:ty, $json:expr) => {
|
|
||||||
assert_value_ok!($type, Default::default(), $json)
|
|
||||||
};
|
|
||||||
($type:ty, $expected:expr, $json:expr) => {{
|
|
||||||
#[derive(Debug, serde::Deserialize, PartialEq)]
|
|
||||||
struct Value {
|
|
||||||
value: $type,
|
|
||||||
}
|
|
||||||
|
|
||||||
let expected = Value { value: $expected };
|
|
||||||
|
|
||||||
let json = format!("value = {}", $json);
|
|
||||||
let actual = from_str::<Value>(&json).unwrap();
|
|
||||||
assert_eq!(actual, expected);
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! assert_ok {
|
|
||||||
($type:ty, $expected:expr, $json:expr) => {{
|
|
||||||
let actual = from_str::<$type>($json).unwrap();
|
|
||||||
assert_eq!(actual, $expected);
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! assert_value_err {
|
|
||||||
($type:ty, $expected:expr, $json:expr) => {{
|
|
||||||
#[derive(Debug, serde::Deserialize, PartialEq)]
|
|
||||||
struct Value {
|
|
||||||
value: $type,
|
|
||||||
}
|
|
||||||
|
|
||||||
let json = format!("value = {}", $json);
|
|
||||||
let actual = from_str::<Value>(&json);
|
|
||||||
assert_eq!(actual, Err($expected));
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn deserialize_null() {
|
|
||||||
assert_value_ok!((), "null");
|
|
||||||
|
|
||||||
let err = Error::new(ErrorCode::ExpectedNull, 1, 8, Some(" foo".to_string()));
|
|
||||||
assert_value_err!((), err, "foo");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn deserialize_bool() {
|
|
||||||
assert_value_ok!(bool, true, "true");
|
|
||||||
assert_value_ok!(bool, false, "false");
|
|
||||||
|
|
||||||
let err = Error::new(ErrorCode::ExpectedBoolean, 1, 8, Some(" foo".to_string()));
|
|
||||||
assert_value_err!(bool, err, "foo");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn deserialize_integer() {
|
|
||||||
assert_value_ok!(i64, 0, "0");
|
|
||||||
assert_value_ok!(i64, -1, "-1");
|
|
||||||
assert_value_ok!(i64, i64::MAX, i64::MAX.to_string());
|
|
||||||
assert_value_ok!(i64, i64::MIN, i64::MIN.to_string());
|
|
||||||
|
|
||||||
assert_value_ok!(i8, 0, "0");
|
|
||||||
assert_value_ok!(i8, 102, "102");
|
|
||||||
assert_value_ok!(i8, -102, "-102");
|
|
||||||
assert_value_ok!(u8, 102, "102");
|
|
||||||
assert_value_ok!(i16, 256, "256");
|
|
||||||
|
|
||||||
let err = Error::new(ErrorCode::ExpectedInteger, 1, 8, Some(" foo".to_string()));
|
|
||||||
assert_value_err!(i64, err, "foo");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn deserialize_float() {
|
|
||||||
assert_value_ok!(f64, 0.0, "0");
|
|
||||||
assert_value_ok!(f64, 0.0, "0.0");
|
|
||||||
assert_value_ok!(f64, -1.0, "-1");
|
|
||||||
assert_value_ok!(f64, -1.0, "-1.0");
|
|
||||||
assert_value_ok!(f64, f64::MAX, f64::MAX.to_string());
|
|
||||||
assert_value_ok!(f64, f64::MIN, f64::MIN.to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn deserialize_vec() {
|
|
||||||
assert_value_ok!(Vec<u64>, vec![1, 2, 3], "[1, 2, 3]");
|
|
||||||
assert_value_ok!(
|
|
||||||
Vec<u64>,
|
|
||||||
vec![1, 2, 3],
|
|
||||||
"\
|
|
||||||
[
|
|
||||||
1
|
|
||||||
2
|
|
||||||
3
|
|
||||||
]"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn deserialize_enum() {
|
|
||||||
#[derive(Debug, serde::Deserialize, PartialEq)]
|
|
||||||
enum Animal {
|
|
||||||
Mouse,
|
|
||||||
Dog { name: String },
|
|
||||||
Cat(u64),
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_value_ok!(Animal, Animal::Mouse, "Mouse");
|
|
||||||
assert_value_ok!(Animal, Animal::Cat(9), "{ Cat = 9 }");
|
|
||||||
assert_value_ok!(
|
|
||||||
Animal,
|
|
||||||
Animal::Dog {
|
|
||||||
name: String::from("Buddy")
|
|
||||||
},
|
|
||||||
"{ Dog = { name = Buddy }}"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checks the example from
|
|
||||||
// https://help.autodesk.com/view/Stingray/ENU/?guid=__stingray_help_managing_content_sjson_html
|
|
||||||
#[test]
|
|
||||||
fn deserialize_stingray_example() {
|
|
||||||
#[derive(Debug, serde::Deserialize, PartialEq)]
|
|
||||||
struct Win32Settings {
|
|
||||||
query_performance_counter_affinity_mask: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, PartialEq)]
|
|
||||||
struct Settings {
|
|
||||||
boot_script: String,
|
|
||||||
console_port: u16,
|
|
||||||
win32: Win32Settings,
|
|
||||||
render_config: PathBuf,
|
|
||||||
}
|
|
||||||
|
|
||||||
let expected = Settings {
|
|
||||||
boot_script: String::from("boot"),
|
|
||||||
console_port: 14030,
|
|
||||||
win32: Win32Settings {
|
|
||||||
query_performance_counter_affinity_mask: 0,
|
|
||||||
},
|
|
||||||
render_config: PathBuf::from("core/rendering/renderer"),
|
|
||||||
};
|
|
||||||
|
|
||||||
let json = r#"
|
|
||||||
// The script that should be started when the application runs.
|
|
||||||
boot_script = "boot"
|
|
||||||
|
|
||||||
// The port on which the console server runs.
|
|
||||||
console_port = 14030
|
|
||||||
|
|
||||||
// Settings for the win32 platform
|
|
||||||
win32 = {
|
|
||||||
/* Sets the affinity mask for
|
|
||||||
QueryPerformanceCounter() */
|
|
||||||
query_performance_counter_affinity_mask = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
render_config = "core/rendering/renderer"
|
|
||||||
"#;
|
|
||||||
|
|
||||||
assert_ok!(Settings, expected, json);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn deserialize_missing_top_level_struct() {
|
|
||||||
let json = "0";
|
|
||||||
let err = Error::new(
|
|
||||||
ErrorCode::ExpectedTopLevelObject,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
Some(json.to_string()),
|
|
||||||
);
|
|
||||||
let actual = from_str::<i64>(json);
|
|
||||||
assert_eq!(actual, Err(err));
|
|
||||||
|
|
||||||
let json = "1.23";
|
|
||||||
let err = Error::new(
|
|
||||||
ErrorCode::ExpectedTopLevelObject,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
Some(json.to_string()),
|
|
||||||
);
|
|
||||||
let actual = from_str::<f64>(json);
|
|
||||||
assert_eq!(actual, Err(err));
|
|
||||||
|
|
||||||
let json = "true";
|
|
||||||
let err = Error::new(
|
|
||||||
ErrorCode::ExpectedTopLevelObject,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
Some(json.to_string()),
|
|
||||||
);
|
|
||||||
let actual = from_str::<bool>(json);
|
|
||||||
assert_eq!(actual, Err(err));
|
|
||||||
|
|
||||||
let json = "null";
|
|
||||||
let err = Error::new(
|
|
||||||
ErrorCode::ExpectedTopLevelObject,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
Some(json.to_string()),
|
|
||||||
);
|
|
||||||
let actual = from_str::<()>(json);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
106
src/error.rs
106
src/error.rs
|
@ -1,74 +1,57 @@
|
||||||
use std::{fmt, io};
|
use std::{fmt, io};
|
||||||
|
|
||||||
use crate::parser::Token;
|
|
||||||
|
|
||||||
/// An alias for a `Result` with `serde_sjson::Error`.
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
/// A type encapsulating the different errors that might occurr
|
|
||||||
/// during serialization or deserialization.
|
|
||||||
#[derive(PartialEq)]
|
|
||||||
pub struct Error {
|
pub struct Error {
|
||||||
inner: Box<ErrorImpl>,
|
inner: Box<ErrorImpl>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
|
||||||
struct ErrorImpl {
|
struct ErrorImpl {
|
||||||
code: ErrorCode,
|
code: ErrorCode,
|
||||||
line: u32,
|
line: usize,
|
||||||
column: usize,
|
column: usize,
|
||||||
fragment: Option<String>,
|
|
||||||
token: Option<Token>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
// TODO: Remove once they are constructed
|
||||||
|
#[allow(dead_code)]
|
||||||
pub(crate) enum ErrorCode {
|
pub(crate) enum ErrorCode {
|
||||||
// Generic error built from a message or different error
|
// Generic error built from a message or different error
|
||||||
Message(String),
|
Message(String),
|
||||||
|
// Wrap inner I/O errors
|
||||||
|
Io(io::Error),
|
||||||
|
Eof,
|
||||||
|
Syntax,
|
||||||
|
ExpectedTopLevelObject,
|
||||||
|
ExpectedBoolean,
|
||||||
|
ExpectedInteger,
|
||||||
|
ExpectedString,
|
||||||
|
ExpectedNull,
|
||||||
ExpectedArray,
|
ExpectedArray,
|
||||||
ExpectedArrayEnd,
|
ExpectedArrayEnd,
|
||||||
ExpectedArraySeparator,
|
|
||||||
ExpectedBoolean,
|
|
||||||
ExpectedEnum,
|
|
||||||
ExpectedFloat,
|
|
||||||
ExpectedInteger,
|
|
||||||
ExpectedMap,
|
ExpectedMap,
|
||||||
ExpectedMapEnd,
|
|
||||||
ExpectedMapEquals,
|
ExpectedMapEquals,
|
||||||
ExpectedMapSeparator,
|
ExpectedMapEnd,
|
||||||
ExpectedNull,
|
|
||||||
ExpectedString,
|
|
||||||
ExpectedTopLevelObject,
|
|
||||||
ExpectedValue,
|
|
||||||
TrailingCharacters,
|
TrailingCharacters,
|
||||||
NonFiniteFloat,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ErrorCode {
|
impl fmt::Display for ErrorCode {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
ErrorCode::Message(msg) => f.write_str(msg),
|
ErrorCode::Message(msg) => f.write_str(msg),
|
||||||
|
ErrorCode::Io(err) => fmt::Display::fmt(err, f),
|
||||||
|
ErrorCode::Eof => f.write_str("unexpected end of input"),
|
||||||
|
ErrorCode::Syntax => f.write_str("syntax error"),
|
||||||
|
ErrorCode::ExpectedTopLevelObject => f.write_str("expected object at the top level"),
|
||||||
|
ErrorCode::ExpectedBoolean => f.write_str("expected a boolean value"),
|
||||||
|
ErrorCode::ExpectedInteger => f.write_str("expected an integer value"),
|
||||||
|
ErrorCode::ExpectedString => f.write_str("expected a string value"),
|
||||||
|
ErrorCode::ExpectedNull => f.write_str("expected null"),
|
||||||
ErrorCode::ExpectedArray => f.write_str("expected an array value"),
|
ErrorCode::ExpectedArray => f.write_str("expected an array value"),
|
||||||
ErrorCode::ExpectedArrayEnd => f.write_str("expected an array end delimiter"),
|
ErrorCode::ExpectedArrayEnd => f.write_str("expected an array end delimiter"),
|
||||||
ErrorCode::ExpectedArraySeparator => {
|
ErrorCode::ExpectedMap => f.write_str("expected an object value"),
|
||||||
f.write_str("expected comma or newline between array entries")
|
|
||||||
}
|
|
||||||
ErrorCode::ExpectedBoolean => f.write_str("expected a boolean value"),
|
|
||||||
ErrorCode::ExpectedEnum => f.write_str("expected string or object"),
|
|
||||||
ErrorCode::ExpectedFloat => f.write_str("expected floating point number"),
|
|
||||||
ErrorCode::ExpectedInteger => f.write_str("expected an integer value"),
|
|
||||||
ErrorCode::ExpectedMap => f.write_str("expected an object"),
|
|
||||||
ErrorCode::ExpectedMapEnd => f.write_str("expected an object end delimiter"),
|
|
||||||
ErrorCode::ExpectedMapEquals => f.write_str("expected a '=' between key and value"),
|
ErrorCode::ExpectedMapEquals => f.write_str("expected a '=' between key and value"),
|
||||||
ErrorCode::ExpectedMapSeparator => {
|
ErrorCode::ExpectedMapEnd => f.write_str("expected an object end delimiter"),
|
||||||
f.write_str("expected comma or newline between object entries")
|
|
||||||
}
|
|
||||||
ErrorCode::ExpectedNull => f.write_str("expected null"),
|
|
||||||
ErrorCode::ExpectedString => f.write_str("expected a string value"),
|
|
||||||
ErrorCode::ExpectedTopLevelObject => f.write_str("expected object at the top level"),
|
|
||||||
ErrorCode::ExpectedValue => f.write_str("expected a value"),
|
|
||||||
ErrorCode::TrailingCharacters => f.write_str("unexpected trailing characters"),
|
ErrorCode::TrailingCharacters => f.write_str("unexpected trailing characters"),
|
||||||
ErrorCode::NonFiniteFloat => f.write_str("got infinite floating point number"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,12 +80,10 @@ 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: {:?}, token: {:?})",
|
"Error({:?}, line: {}, column: {})",
|
||||||
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.token,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,8 +97,6 @@ impl serde::de::Error for Error {
|
||||||
code: ErrorCode::Message(msg.to_string()),
|
code: ErrorCode::Message(msg.to_string()),
|
||||||
line: 0,
|
line: 0,
|
||||||
column: 0,
|
column: 0,
|
||||||
fragment: None,
|
|
||||||
token: None,
|
|
||||||
});
|
});
|
||||||
Self { inner }
|
Self { inner }
|
||||||
}
|
}
|
||||||
|
@ -132,8 +111,6 @@ impl serde::ser::Error for Error {
|
||||||
code: ErrorCode::Message(msg.to_string()),
|
code: ErrorCode::Message(msg.to_string()),
|
||||||
line: 0,
|
line: 0,
|
||||||
column: 0,
|
column: 0,
|
||||||
fragment: None,
|
|
||||||
token: None,
|
|
||||||
});
|
});
|
||||||
Self { inner }
|
Self { inner }
|
||||||
}
|
}
|
||||||
|
@ -142,38 +119,9 @@ impl serde::ser::Error for Error {
|
||||||
impl std::error::Error for Error {}
|
impl std::error::Error for Error {}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
pub(crate) fn new(code: ErrorCode, line: u32, column: usize, fragment: Option<String>) -> Self {
|
pub(crate) fn new(code: ErrorCode, line: usize, column: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner: Box::new(ErrorImpl {
|
inner: Box::new(ErrorImpl { code, line, column }),
|
||||||
code,
|
|
||||||
line,
|
|
||||||
column,
|
|
||||||
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),
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<io::Error> for Error {
|
|
||||||
fn from(err: io::Error) -> Self {
|
|
||||||
Self::new(ErrorCode::Message(format!("{}", err)), 0, 0, None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
75
src/lib.rs
75
src/lib.rs
|
@ -1,76 +1,7 @@
|
||||||
//! A **ser**ialization/**de**serialization library for Simplified JSON,
|
// mod de;
|
||||||
//! the Bitsquid/Stingray flavor of JSON.
|
|
||||||
//!
|
|
||||||
//! # Usage
|
|
||||||
//!
|
|
||||||
//! ## Serializing
|
|
||||||
//!
|
|
||||||
//! ```
|
|
||||||
//! use serde::Serialize;
|
|
||||||
//! use serde_sjson::Result;
|
|
||||||
//!
|
|
||||||
//! #[derive(Serialize)]
|
|
||||||
//! struct Person {
|
|
||||||
//! name: String,
|
|
||||||
//! age: u8,
|
|
||||||
//! friends: Vec<String>,
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! fn main() -> Result<()> {
|
|
||||||
//! let data = Person {
|
|
||||||
//! name: String::from("Marc"),
|
|
||||||
//! age: 21,
|
|
||||||
//! friends: vec![String::from("Jessica"), String::from("Paul")],
|
|
||||||
//! };
|
|
||||||
//!
|
|
||||||
//! let s = serde_sjson::to_string(&data)?;
|
|
||||||
//!
|
|
||||||
//! println!("{}", s);
|
|
||||||
//!
|
|
||||||
//! Ok(())
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! ## Deserializing
|
|
||||||
//!
|
|
||||||
//! ```
|
|
||||||
//! use serde::Deserialize;
|
|
||||||
//! use serde_sjson::Result;
|
|
||||||
//!
|
|
||||||
//! #[derive(Deserialize)]
|
|
||||||
//! struct Person {
|
|
||||||
//! name: String,
|
|
||||||
//! age: u8,
|
|
||||||
//! friends: Vec<String>,
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! fn main() -> Result<()> {
|
|
||||||
//! let sjson = r#"
|
|
||||||
//! name = Marc
|
|
||||||
//! age = 21
|
|
||||||
//! friends = [
|
|
||||||
//! Jessica
|
|
||||||
//! Paul
|
|
||||||
//! ]"#;
|
|
||||||
//!
|
|
||||||
//! let data: Person = serde_sjson::from_str(sjson)?;
|
|
||||||
//!
|
|
||||||
//! println!(
|
|
||||||
//! "{} is {} years old and has {} friends.",
|
|
||||||
//! data.name,
|
|
||||||
//! data.age,
|
|
||||||
//! data.friends.len()
|
|
||||||
//! );
|
|
||||||
//!
|
|
||||||
//! Ok(())
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
|
|
||||||
mod de;
|
|
||||||
mod error;
|
mod error;
|
||||||
mod parser;
|
|
||||||
mod ser;
|
mod ser;
|
||||||
|
|
||||||
pub use de::{from_str, Deserializer};
|
// pub use de::{from_str, Deserializer};
|
||||||
pub use error::{Error, Result};
|
pub use error::{Error, Result};
|
||||||
pub use ser::{to_string, to_vec, to_writer, Serializer};
|
pub use ser::{to_string, Serializer};
|
||||||
|
|
458
src/parser.rs
458
src/parser.rs
|
@ -1,458 +0,0 @@
|
||||||
use nom::branch::alt;
|
|
||||||
use nom::bytes::complete::{tag, take_until};
|
|
||||||
use nom::character::complete::{char, digit1, none_of, not_line_ending, one_of};
|
|
||||||
use nom::combinator::{cut, eof, map, map_res, opt, recognize, value};
|
|
||||||
use nom::multi::many1_count;
|
|
||||||
use nom::number::complete::double;
|
|
||||||
use nom::sequence::{delimited, preceded, terminated, tuple};
|
|
||||||
use nom::{IResult, Slice};
|
|
||||||
use nom_locate::LocatedSpan;
|
|
||||||
|
|
||||||
pub(crate) type Span<'a> = LocatedSpan<&'a str>;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub(crate) enum Token {
|
|
||||||
ArrayEnd,
|
|
||||||
ArrayStart,
|
|
||||||
Boolean(bool),
|
|
||||||
Eof,
|
|
||||||
Equals,
|
|
||||||
Float(f64),
|
|
||||||
Integer(i64),
|
|
||||||
Null,
|
|
||||||
ObjectEnd,
|
|
||||||
ObjectStart,
|
|
||||||
Separator,
|
|
||||||
String(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
fn horizontal_whitespace(input: Span) -> IResult<Span, char> {
|
|
||||||
one_of(" \t")(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn whitespace(input: Span) -> IResult<Span, char> {
|
|
||||||
one_of(" \n\r\t")(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn null(input: Span) -> IResult<Span, ()> {
|
|
||||||
value((), tag("null"))(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn separator(input: Span) -> IResult<Span, &str> {
|
|
||||||
map(alt((tag(","), tag("\n"), tag("\r\n"))), |val: Span| {
|
|
||||||
*val.fragment()
|
|
||||||
})(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bool(input: Span) -> IResult<Span, bool> {
|
|
||||||
alt((value(true, tag("true")), value(false, tag("false"))))(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn integer(input: Span) -> IResult<Span, i64> {
|
|
||||||
map_res(recognize(tuple((opt(char('-')), digit1))), |val: Span| {
|
|
||||||
val.fragment().parse::<i64>()
|
|
||||||
})(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn float(input: Span) -> IResult<Span, f64> {
|
|
||||||
double(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn identifier(input: Span) -> IResult<Span, &str> {
|
|
||||||
map(recognize(many1_count(none_of("\" \t\n=:"))), |val: Span| {
|
|
||||||
*val.fragment()
|
|
||||||
})(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn literal_string(input: Span) -> IResult<Span, &str> {
|
|
||||||
map(
|
|
||||||
delimited(tag("\"\"\""), take_until("\"\"\""), tag("\"\"\"")),
|
|
||||||
|val: Span| *val.fragment(),
|
|
||||||
)(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn string_content(input: Span) -> IResult<Span, &str> {
|
|
||||||
let buf = input.fragment();
|
|
||||||
let mut escaped = false;
|
|
||||||
let mut i = 0;
|
|
||||||
|
|
||||||
for (j, ch) in buf.char_indices() {
|
|
||||||
i = j;
|
|
||||||
match ch {
|
|
||||||
'\\' if !escaped => {
|
|
||||||
escaped = true;
|
|
||||||
}
|
|
||||||
'\n' if !escaped => {
|
|
||||||
let err = nom::error::Error {
|
|
||||||
input: input.slice(j..),
|
|
||||||
code: nom::error::ErrorKind::Char,
|
|
||||||
};
|
|
||||||
return Err(nom::Err::Error(err));
|
|
||||||
}
|
|
||||||
'"' if !escaped => {
|
|
||||||
return Ok((input.slice(j..), &buf[0..j]));
|
|
||||||
}
|
|
||||||
_ => escaped = false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let err = nom::error::Error {
|
|
||||||
input: input.slice((i + 1)..),
|
|
||||||
code: nom::error::ErrorKind::Char,
|
|
||||||
};
|
|
||||||
Err(nom::Err::Failure(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn delimited_string(input: Span) -> IResult<Span, &str> {
|
|
||||||
preceded(char('"'), cut(terminated(string_content, char('"'))))(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn string(input: Span) -> IResult<Span, &str> {
|
|
||||||
alt((identifier, literal_string, delimited_string))(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn line_comment(input: Span) -> IResult<Span, &str> {
|
|
||||||
map(
|
|
||||||
preceded(tag("//"), alt((not_line_ending, eof))),
|
|
||||||
|val: Span| *val.fragment(),
|
|
||||||
)(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn block_comment(input: Span) -> IResult<Span, &str> {
|
|
||||||
map(
|
|
||||||
delimited(tag("/*"), take_until("*/"), tag("*/")),
|
|
||||||
|val: Span| *val.fragment(),
|
|
||||||
)(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn comment(input: Span) -> IResult<Span, &str> {
|
|
||||||
alt((line_comment, block_comment))(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn optional(input: Span) -> IResult<Span, ()> {
|
|
||||||
let whitespace = value((), whitespace);
|
|
||||||
let comment = value((), comment);
|
|
||||||
let empty = value((), tag(""));
|
|
||||||
let content = value((), many1_count(alt((whitespace, comment))));
|
|
||||||
|
|
||||||
alt((content, empty))(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn parse_next_token(input: Span) -> IResult<Span, Token> {
|
|
||||||
preceded(
|
|
||||||
opt(optional),
|
|
||||||
alt((
|
|
||||||
// Order is important here.
|
|
||||||
// Certain valid strings like "null", "true" or "false" need to be
|
|
||||||
// matched to their special value.
|
|
||||||
// Integer-like numbers need to be matched to that, but are valid floats, too.
|
|
||||||
value(Token::Eof, eof),
|
|
||||||
value(Token::Separator, separator),
|
|
||||||
value(Token::ObjectStart, tag("{")),
|
|
||||||
value(Token::ObjectEnd, tag("}")),
|
|
||||||
value(Token::ArrayStart, tag("[")),
|
|
||||||
value(Token::ArrayEnd, tag("]")),
|
|
||||||
value(Token::Equals, tag("=")),
|
|
||||||
value(Token::Null, null),
|
|
||||||
map(bool, Token::Boolean),
|
|
||||||
map(integer, Token::Integer),
|
|
||||||
map(float, Token::Float),
|
|
||||||
map(string, |val| Token::String(val.to_string())),
|
|
||||||
)),
|
|
||||||
)(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn parse_trailing_characters(input: Span) -> IResult<Span, ()> {
|
|
||||||
value((), optional)(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn parse_null(input: Span) -> IResult<Span, Token> {
|
|
||||||
preceded(optional, value(Token::Null, null))(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn parse_separator(input: Span) -> IResult<Span, Token> {
|
|
||||||
preceded(
|
|
||||||
opt(horizontal_whitespace),
|
|
||||||
value(Token::Separator, separator),
|
|
||||||
)(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn parse_bool(input: Span) -> IResult<Span, Token> {
|
|
||||||
preceded(optional, map(bool, Token::Boolean))(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn parse_integer(input: Span) -> IResult<Span, Token> {
|
|
||||||
preceded(optional, map(integer, Token::Integer))(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn parse_float(input: Span) -> IResult<Span, Token> {
|
|
||||||
preceded(optional, map(float, Token::Float))(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn parse_identifier(input: Span) -> IResult<Span, Token> {
|
|
||||||
preceded(
|
|
||||||
optional,
|
|
||||||
map(identifier, |val| Token::String(val.to_string())),
|
|
||||||
)(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn parse_string(input: Span) -> IResult<Span, Token> {
|
|
||||||
preceded(optional, map(string, |val| Token::String(val.to_string())))(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use nom::error::{Error, ErrorKind};
|
|
||||||
use nom::Err;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
macro_rules! assert_ok {
|
|
||||||
($input:expr, $parser:ident, $remain:expr, $output:expr) => {{
|
|
||||||
let res = super::$parser(Span::from($input));
|
|
||||||
assert_eq!(
|
|
||||||
res.map(|(span, res)| { (*span, res) }),
|
|
||||||
Ok(($remain, $output))
|
|
||||||
);
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! assert_err {
|
|
||||||
($input:expr, $parser:ident, $kind:expr) => {{
|
|
||||||
{
|
|
||||||
let input = Span::from($input);
|
|
||||||
assert_eq!(
|
|
||||||
super::$parser(input),
|
|
||||||
Err(Err::Error(Error::new(input, $kind)))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
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]
|
|
||||||
fn parse_optional() {
|
|
||||||
assert_ok!("\n", whitespace, "", '\n');
|
|
||||||
assert_ok!("\t", whitespace, "", '\t');
|
|
||||||
assert_ok!(" ", whitespace, " ", ' ');
|
|
||||||
assert_ok!("/* foo bar */", comment, "", " foo bar ");
|
|
||||||
assert_ok!("// foo", comment, "", " foo");
|
|
||||||
assert_ok!("// foo\n", comment, "\n", " foo");
|
|
||||||
|
|
||||||
assert_ok!("", optional, "", ());
|
|
||||||
assert_ok!("\t\n", optional, "", ());
|
|
||||||
assert_ok!("\n\t", optional, "", ());
|
|
||||||
assert_ok!("// foo", optional, "", ());
|
|
||||||
assert_ok!("\n\t// foo\n\t/* foo\n\tbar */\n", optional, "", ());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parse_integer() {
|
|
||||||
assert_ok!("3", integer, "", 3);
|
|
||||||
assert_ok!("12345", integer, "", 12345);
|
|
||||||
assert_ok!("-12345", integer, "", -12345);
|
|
||||||
assert_ok!("12345 ", integer, " ", 12345);
|
|
||||||
|
|
||||||
assert_err!(" 12345", integer, ErrorKind::Digit);
|
|
||||||
|
|
||||||
assert_ok!(" 12345", parse_integer, "", Token::Integer(12345));
|
|
||||||
assert_ok!("\n12345", parse_integer, "", Token::Integer(12345));
|
|
||||||
assert_ok!("\t12345", parse_integer, "", Token::Integer(12345));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parse_float() {
|
|
||||||
assert_ok!("3", float, "", 3.0);
|
|
||||||
assert_ok!("3.0", float, "", 3.0);
|
|
||||||
assert_ok!("3.1415", float, "", 3.1415);
|
|
||||||
assert_ok!("-123.456789", float, "", -123.456789);
|
|
||||||
assert_err!(" 1.23", float, ErrorKind::Float);
|
|
||||||
assert_ok!("1.23 ", float, " ", 1.23);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parse_raw_string() {
|
|
||||||
assert_ok!("foo", identifier, "", "foo");
|
|
||||||
assert_ok!("foo123", identifier, "", "foo123");
|
|
||||||
assert_ok!("foo_bar", identifier, "", "foo_bar");
|
|
||||||
assert_ok!("_foo", identifier, "", "_foo");
|
|
||||||
assert_ok!("foo bar", identifier, " bar", "foo");
|
|
||||||
assert_ok!("123", identifier, "", "123");
|
|
||||||
assert_ok!("1foo", identifier, "", "1foo");
|
|
||||||
assert_ok!("foo-bar", identifier, "", "foo-bar");
|
|
||||||
assert_ok!("foo/bar", identifier, "", "foo/bar");
|
|
||||||
assert_ok!("foo\"", identifier, "\"", "foo");
|
|
||||||
|
|
||||||
assert_err!("\"foo", identifier, ErrorKind::Many1Count);
|
|
||||||
assert_err!("\"foo\"", identifier, ErrorKind::Many1Count);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parse_delimited_string() {
|
|
||||||
assert_ok!(r#""""#, delimited_string, "", "");
|
|
||||||
assert_ok!(r#""foo""#, delimited_string, "", "foo");
|
|
||||||
assert_ok!(r#""\"foo""#, delimited_string, "", r#"\"foo"#);
|
|
||||||
assert_ok!(r#""foo bar""#, delimited_string, "", "foo bar");
|
|
||||||
assert_ok!(r#""foo123""#, delimited_string, "", "foo123");
|
|
||||||
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);
|
|
||||||
|
|
||||||
{
|
|
||||||
let input = Span::from("\"foo");
|
|
||||||
assert_eq!(
|
|
||||||
delimited_string(input),
|
|
||||||
Err(Err::Failure(Error::new(
|
|
||||||
unsafe { Span::new_from_raw_offset(4, 1, "", ()) },
|
|
||||||
ErrorKind::Char
|
|
||||||
)))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
let input = Span::from("\"foo\nbar\"");
|
|
||||||
assert_eq!(
|
|
||||||
delimited_string(input),
|
|
||||||
Err(Err::Failure(Error::new(
|
|
||||||
unsafe { Span::new_from_raw_offset(4, 1, "\nbar\"", ()) },
|
|
||||||
ErrorKind::Char
|
|
||||||
)))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parse_literal_string() {
|
|
||||||
assert_ok!(r#""""""""#, literal_string, "", "");
|
|
||||||
assert_ok!(r#""""foo""""#, literal_string, "", "foo");
|
|
||||||
assert_ok!(r#""""foo"""""#, literal_string, "\"", "foo");
|
|
||||||
assert_ok!(r#"""""foo""""#, literal_string, "", "\"foo");
|
|
||||||
assert_ok!(r#""""\n""""#, literal_string, "", "\\n");
|
|
||||||
|
|
||||||
{
|
|
||||||
let raw = r#"
|
|
||||||
This is a lengthy description!
|
|
||||||
|
|
||||||
It contains line breaks.
|
|
||||||
|
|
||||||
Escape sequences, like \n and \t, are parsed literally.
|
|
||||||
"Quoted strings are fine", so are two sucessive quotes: "".
|
|
||||||
"#;
|
|
||||||
|
|
||||||
let input = format!(r#""""{}""""#, raw);
|
|
||||||
|
|
||||||
assert_ok!(input.as_str(), literal_string, "", raw);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
let input = Span::from(r#"""""""#);
|
|
||||||
assert_eq!(
|
|
||||||
literal_string(input),
|
|
||||||
Err(Err::Error(Error::new(
|
|
||||||
unsafe { Span::new_from_raw_offset(3, 1, "\"\"", ()) },
|
|
||||||
ErrorKind::TakeUntil
|
|
||||||
)))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parse_line_comment() {
|
|
||||||
assert_ok!("// foo", line_comment, "", " foo");
|
|
||||||
assert_ok!("// foo\n", line_comment, "\n", " foo");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parse_block_comment() {
|
|
||||||
assert_ok!("/* foo */", block_comment, "", " foo ");
|
|
||||||
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,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Regression test for #2
|
|
||||||
#[test]
|
|
||||||
fn parse_windows_path() {
|
|
||||||
let text = "C:\\Users\\public\\test.txt";
|
|
||||||
let sjson = format!(r#""{}""#, text);
|
|
||||||
check_parse_result(sjson, [Token::String(String::from(text))]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Regression test for #10
|
|
||||||
#[test]
|
|
||||||
fn parse_crlf_separator() {
|
|
||||||
let sjson = "foo = 1\r\nbar = 2";
|
|
||||||
check_parse_result(
|
|
||||||
sjson,
|
|
||||||
[
|
|
||||||
Token::String(String::from("foo")),
|
|
||||||
Token::Equals,
|
|
||||||
Token::Integer(1),
|
|
||||||
Token::String(String::from("bar")),
|
|
||||||
Token::Equals,
|
|
||||||
Token::Integer(2),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
402
src/ser.rs
402
src/ser.rs
|
@ -1,93 +1,46 @@
|
||||||
use std::io;
|
|
||||||
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use crate::error::{Error, ErrorCode, Result};
|
use crate::error::{Error, ErrorCode, Result};
|
||||||
|
|
||||||
// TODO: Make configurable
|
// TODO: Make configurable
|
||||||
const INDENT: [u8; 2] = [0x20, 0x20];
|
const INDENT: &str = " ";
|
||||||
|
|
||||||
/// A container for serializing Rust values into SJSON.
|
pub struct Serializer {
|
||||||
pub struct Serializer<W> {
|
|
||||||
// The current indentation level
|
// The current indentation level
|
||||||
level: usize,
|
level: usize,
|
||||||
writer: W,
|
// The output string
|
||||||
|
output: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serializes a value into a generic `io::Write`.
|
|
||||||
#[inline]
|
|
||||||
pub fn to_writer<T, W>(writer: &mut W, value: &T) -> Result<()>
|
|
||||||
where
|
|
||||||
W: io::Write,
|
|
||||||
T: Serialize,
|
|
||||||
{
|
|
||||||
let mut serializer = Serializer::new(writer);
|
|
||||||
value.serialize(&mut serializer)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Serializes a value into a byte vector.
|
|
||||||
#[inline]
|
|
||||||
pub fn to_vec<T>(value: &T) -> Result<Vec<u8>>
|
|
||||||
where
|
|
||||||
T: Serialize,
|
|
||||||
{
|
|
||||||
let mut vec = Vec::with_capacity(128);
|
|
||||||
to_writer(&mut vec, value)?;
|
|
||||||
Ok(vec)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Serializes a value into a string.
|
|
||||||
#[inline]
|
|
||||||
pub fn to_string<T>(value: &T) -> Result<String>
|
pub fn to_string<T>(value: &T) -> Result<String>
|
||||||
where
|
where
|
||||||
T: Serialize,
|
T: Serialize,
|
||||||
{
|
{
|
||||||
let vec = to_vec(value)?;
|
let mut serializer = Serializer {
|
||||||
let string = if cfg!(debug_assertions) {
|
level: 0,
|
||||||
String::from_utf8(vec).expect("We do not emit invalid UTF-8")
|
output: String::new(),
|
||||||
} else {
|
|
||||||
unsafe { String::from_utf8_unchecked(vec) }
|
|
||||||
};
|
};
|
||||||
Ok(string)
|
value.serialize(&mut serializer)?;
|
||||||
|
Ok(serializer.output)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W> Serializer<W>
|
impl Serializer {
|
||||||
where
|
fn add_indent(&mut self) {
|
||||||
W: io::Write,
|
|
||||||
{
|
|
||||||
/// Creates a new `Serializer`.
|
|
||||||
pub fn new(writer: W) -> Self {
|
|
||||||
Self { level: 0, writer }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn write(&mut self, bytes: impl AsRef<[u8]>) -> Result<()> {
|
|
||||||
self.writer.write_all(bytes.as_ref()).map_err(Error::from)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn add_indent(&mut self) -> Result<()> {
|
|
||||||
for _ in 0..self.level.saturating_sub(1) {
|
for _ in 0..self.level.saturating_sub(1) {
|
||||||
self.write(INDENT)?;
|
self.output += INDENT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn ensure_top_level_struct(&self) -> Result<()> {
|
fn ensure_top_level_struct(&self) -> Result<()> {
|
||||||
if self.level == 0 {
|
if self.level == 0 {
|
||||||
return Err(Error::new(ErrorCode::ExpectedTopLevelObject, 0, 0, None));
|
return Err(Error::new(ErrorCode::ExpectedTopLevelObject, 0, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, W> serde::ser::Serializer for &'a mut Serializer<W>
|
impl<'a> serde::ser::Serializer for &'a mut Serializer {
|
||||||
where
|
|
||||||
W: io::Write,
|
|
||||||
{
|
|
||||||
type Ok = ();
|
type Ok = ();
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
|
@ -101,7 +54,7 @@ where
|
||||||
|
|
||||||
fn serialize_bool(self, v: bool) -> Result<Self::Ok> {
|
fn serialize_bool(self, v: bool) -> Result<Self::Ok> {
|
||||||
self.ensure_top_level_struct()?;
|
self.ensure_top_level_struct()?;
|
||||||
self.write(if v { "true" } else { "false" })?;
|
self.output += if v { "true" } else { "false" };
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,7 +72,8 @@ where
|
||||||
|
|
||||||
fn serialize_i64(self, v: i64) -> Result<Self::Ok> {
|
fn serialize_i64(self, v: i64) -> Result<Self::Ok> {
|
||||||
self.ensure_top_level_struct()?;
|
self.ensure_top_level_struct()?;
|
||||||
self.serialize_str(&format!("{}", v))
|
self.output += &v.to_string();
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_u8(self, v: u8) -> Result<Self::Ok> {
|
fn serialize_u8(self, v: u8) -> Result<Self::Ok> {
|
||||||
|
@ -136,80 +90,90 @@ where
|
||||||
|
|
||||||
fn serialize_u64(self, v: u64) -> Result<Self::Ok> {
|
fn serialize_u64(self, v: u64) -> Result<Self::Ok> {
|
||||||
self.ensure_top_level_struct()?;
|
self.ensure_top_level_struct()?;
|
||||||
self.serialize_str(&format!("{}", v))
|
self.output += &v.to_string();
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_f32(self, v: f32) -> Result<Self::Ok> {
|
fn serialize_f32(self, v: f32) -> Result<Self::Ok> {
|
||||||
|
if v.is_finite() {
|
||||||
self.serialize_f64(v.into())
|
self.serialize_f64(v.into())
|
||||||
|
} else {
|
||||||
|
self.ensure_top_level_struct()?;
|
||||||
|
self.output += "null";
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_f64(self, v: f64) -> Result<Self::Ok> {
|
fn serialize_f64(self, v: f64) -> Result<Self::Ok> {
|
||||||
self.ensure_top_level_struct()?;
|
self.ensure_top_level_struct()?;
|
||||||
if !v.is_finite() {
|
if v.is_finite() {
|
||||||
return Err(Error::new(ErrorCode::NonFiniteFloat, 0, 0, None));
|
self.output += &v.to_string();
|
||||||
|
} else {
|
||||||
|
self.output += "null";
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
self.serialize_str(&format!("{}", v))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_char(self, v: char) -> Result<Self::Ok> {
|
fn serialize_char(self, v: char) -> Result<Self::Ok> {
|
||||||
let mut buf = [0; 4];
|
let mut buf = [0; 4];
|
||||||
self.serialize_bytes(v.encode_utf8(&mut buf).as_bytes())
|
self.serialize_str(v.encode_utf8(&mut buf))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_str(self, v: &str) -> Result<Self::Ok> {
|
fn serialize_str(self, v: &str) -> Result<Self::Ok> {
|
||||||
self.ensure_top_level_struct()?;
|
self.ensure_top_level_struct()?;
|
||||||
|
let needs_escapes =
|
||||||
|
v.is_empty() || v.contains([' ', '\n', '\r', '\t', '=', '\'', '"', '\\', '/']);
|
||||||
|
if needs_escapes {
|
||||||
|
self.output += "\"";
|
||||||
|
|
||||||
let needs_quotes =
|
let len = v.len();
|
||||||
v.is_empty() || v.contains([' ', '\n', '\r', '\t', '=', '\'', '"', '\\', ':']);
|
let chars = v.chars();
|
||||||
|
let mut start = 0;
|
||||||
|
|
||||||
if needs_quotes {
|
for (i, c) in chars.enumerate() {
|
||||||
self.write(b"\"")?;
|
if ('\x20'..='\x7e').contains(&c)
|
||||||
|
&& !['\t', '\n', '\r', '\"', '\\', '/'].contains(&c)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.output += &v[start..i];
|
||||||
|
self.output.push('\\');
|
||||||
|
|
||||||
// Since we've added a layer of quotes, we now need to escape
|
|
||||||
// certain characters.
|
|
||||||
for c in v.chars() {
|
|
||||||
match c {
|
match c {
|
||||||
'\t' => {
|
'\t' => {
|
||||||
self.write(b"\\")?;
|
self.output.push('t');
|
||||||
self.write(b"t")?;
|
|
||||||
}
|
}
|
||||||
'\n' => {
|
'\n' => {
|
||||||
self.write(b"\\")?;
|
self.output.push('n');
|
||||||
self.write(b"n")?;
|
|
||||||
}
|
}
|
||||||
'\r' => {
|
'\r' => {
|
||||||
self.write(b"\\")?;
|
self.output.push('r');
|
||||||
self.write(b"r")?;
|
|
||||||
}
|
}
|
||||||
'"' => {
|
'\x7f'.. => {
|
||||||
self.write(b"\\")?;
|
self.output += &format!("u{:4x}", c as u32);
|
||||||
self.write(b"\"")?;
|
|
||||||
}
|
|
||||||
'\\' => {
|
|
||||||
self.write(b"\\")?;
|
|
||||||
self.write(b"\\")?;
|
|
||||||
}
|
}
|
||||||
c => {
|
c => {
|
||||||
self.serialize_char(c)?;
|
self.output.push(c);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
start = i + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.write(b"\"")?;
|
if start < len {
|
||||||
|
self.output += &v[start..];
|
||||||
|
}
|
||||||
|
|
||||||
|
self.output += "\"";
|
||||||
} else {
|
} else {
|
||||||
self.write(v.as_bytes())?;
|
self.output += v;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok> {
|
fn serialize_bytes(self, _v: &[u8]) -> Result<Self::Ok> {
|
||||||
self.ensure_top_level_struct()?;
|
todo!()
|
||||||
// For now we assume that the byte array contains
|
|
||||||
// valid SJSON.
|
|
||||||
// TODO: Turn this into an actual array of encoded bytes.
|
|
||||||
self.write(v)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_none(self) -> Result<Self::Ok> {
|
fn serialize_none(self) -> Result<Self::Ok> {
|
||||||
|
@ -229,7 +193,8 @@ where
|
||||||
|
|
||||||
fn serialize_unit(self) -> Result<Self::Ok> {
|
fn serialize_unit(self) -> Result<Self::Ok> {
|
||||||
self.ensure_top_level_struct()?;
|
self.ensure_top_level_struct()?;
|
||||||
self.write(b"null")
|
self.output += "null";
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok> {
|
fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok> {
|
||||||
|
@ -267,18 +232,19 @@ where
|
||||||
{
|
{
|
||||||
self.ensure_top_level_struct()?;
|
self.ensure_top_level_struct()?;
|
||||||
|
|
||||||
self.write(b"{ ")?;
|
self.output += "{ ";
|
||||||
variant.serialize(&mut *self)?;
|
variant.serialize(&mut *self)?;
|
||||||
self.write(b" = ")?;
|
self.output += " = ";
|
||||||
value.serialize(&mut *self)?;
|
value.serialize(&mut *self)?;
|
||||||
self.write(b" }")
|
self.output += " }\n";
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serialize the start of a sequence.
|
// Serialize the start of a sequence.
|
||||||
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
|
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
|
||||||
self.ensure_top_level_struct()?;
|
self.ensure_top_level_struct()?;
|
||||||
|
|
||||||
self.write(b"[\n")?;
|
self.output += "[\n";
|
||||||
self.level += 1;
|
self.level += 1;
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
@ -309,7 +275,7 @@ where
|
||||||
|
|
||||||
variant.serialize(&mut *self)?;
|
variant.serialize(&mut *self)?;
|
||||||
|
|
||||||
self.write(b" = [\n")?;
|
self.output += " = [\n";
|
||||||
self.level += 1;
|
self.level += 1;
|
||||||
|
|
||||||
Ok(self)
|
Ok(self)
|
||||||
|
@ -317,7 +283,7 @@ where
|
||||||
|
|
||||||
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
|
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
|
||||||
if self.level > 0 {
|
if self.level > 0 {
|
||||||
self.write(b"{\n")?;
|
self.output += "{\n";
|
||||||
}
|
}
|
||||||
self.level += 1;
|
self.level += 1;
|
||||||
Ok(self)
|
Ok(self)
|
||||||
|
@ -339,7 +305,7 @@ where
|
||||||
|
|
||||||
variant.serialize(&mut *self)?;
|
variant.serialize(&mut *self)?;
|
||||||
|
|
||||||
self.write(b" = {\n")?;
|
self.output += " = {\n";
|
||||||
self.level += 1;
|
self.level += 1;
|
||||||
|
|
||||||
Ok(self)
|
Ok(self)
|
||||||
|
@ -353,10 +319,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, W> serde::ser::SerializeSeq for &'a mut Serializer<W>
|
impl<'a> serde::ser::SerializeSeq for &'a mut Serializer {
|
||||||
where
|
|
||||||
W: io::Write,
|
|
||||||
{
|
|
||||||
type Ok = ();
|
type Ok = ();
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
|
@ -364,100 +327,100 @@ where
|
||||||
where
|
where
|
||||||
T: Serialize,
|
T: Serialize,
|
||||||
{
|
{
|
||||||
self.add_indent()?;
|
self.add_indent();
|
||||||
value.serialize(&mut **self)?;
|
value.serialize(&mut **self)?;
|
||||||
self.write(b"\n")
|
if !self.output.ends_with('\n') {
|
||||||
|
self.output += "\n";
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn end(self) -> Result<Self::Ok> {
|
fn end(self) -> Result<Self::Ok> {
|
||||||
self.level -= 1;
|
self.level -= 1;
|
||||||
self.add_indent()?;
|
self.add_indent();
|
||||||
self.write(b"]")
|
self.output += "]\n";
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, W> serde::ser::SerializeTuple for &'a mut Serializer<W>
|
|
||||||
where
|
|
||||||
W: io::Write,
|
|
||||||
{
|
|
||||||
type Ok = ();
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<()>
|
|
||||||
where
|
|
||||||
T: Serialize,
|
|
||||||
{
|
|
||||||
self.add_indent()?;
|
|
||||||
value.serialize(&mut **self)?;
|
|
||||||
self.write(b"\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn end(self) -> Result<Self::Ok> {
|
|
||||||
self.level -= 1;
|
|
||||||
self.add_indent()?;
|
|
||||||
self.write(b"]")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, W> serde::ser::SerializeTupleStruct for &'a mut Serializer<W>
|
|
||||||
where
|
|
||||||
W: io::Write,
|
|
||||||
{
|
|
||||||
type Ok = ();
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<()>
|
|
||||||
where
|
|
||||||
T: Serialize,
|
|
||||||
{
|
|
||||||
self.add_indent()?;
|
|
||||||
value.serialize(&mut **self)?;
|
|
||||||
self.write(b"\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn end(self) -> Result<Self::Ok> {
|
|
||||||
self.level -= 1;
|
|
||||||
self.add_indent()?;
|
|
||||||
self.write(b"]")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, W> serde::ser::SerializeTupleVariant for &'a mut Serializer<W>
|
|
||||||
where
|
|
||||||
W: io::Write,
|
|
||||||
{
|
|
||||||
type Ok = ();
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<()>
|
|
||||||
where
|
|
||||||
T: Serialize,
|
|
||||||
{
|
|
||||||
self.add_indent()?;
|
|
||||||
value.serialize(&mut **self)?;
|
|
||||||
self.write(b"\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn end(self) -> Result<Self::Ok> {
|
|
||||||
self.level -= 1;
|
|
||||||
self.add_indent()?;
|
|
||||||
self.write(b"]\n")?;
|
|
||||||
|
|
||||||
self.level -= 1;
|
|
||||||
|
|
||||||
if self.level > 0 {
|
|
||||||
self.add_indent()?;
|
|
||||||
self.write(b"}")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, W> serde::ser::SerializeMap for &'a mut Serializer<W>
|
impl<'a> serde::ser::SerializeTuple for &'a mut Serializer {
|
||||||
|
type Ok = ();
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<()>
|
||||||
where
|
where
|
||||||
W: io::Write,
|
T: Serialize,
|
||||||
{
|
{
|
||||||
|
self.add_indent();
|
||||||
|
value.serialize(&mut **self)?;
|
||||||
|
if !self.output.ends_with('\n') {
|
||||||
|
self.output += "\n";
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn end(self) -> Result<Self::Ok> {
|
||||||
|
self.level -= 1;
|
||||||
|
self.add_indent();
|
||||||
|
self.output += "]\n";
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> serde::ser::SerializeTupleStruct for &'a mut Serializer {
|
||||||
|
type Ok = ();
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<()>
|
||||||
|
where
|
||||||
|
T: Serialize,
|
||||||
|
{
|
||||||
|
self.add_indent();
|
||||||
|
value.serialize(&mut **self)?;
|
||||||
|
if !self.output.ends_with('\n') {
|
||||||
|
self.output += "\n";
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn end(self) -> Result<Self::Ok> {
|
||||||
|
self.level -= 1;
|
||||||
|
self.add_indent();
|
||||||
|
self.output += "]\n";
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> serde::ser::SerializeTupleVariant for &'a mut Serializer {
|
||||||
|
type Ok = ();
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<()>
|
||||||
|
where
|
||||||
|
T: Serialize,
|
||||||
|
{
|
||||||
|
self.add_indent();
|
||||||
|
value.serialize(&mut **self)?;
|
||||||
|
if !self.output.ends_with('\n') {
|
||||||
|
self.output += "\n";
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn end(self) -> Result<Self::Ok> {
|
||||||
|
self.level -= 1;
|
||||||
|
self.add_indent();
|
||||||
|
self.output += "]\n";
|
||||||
|
self.level -= 1;
|
||||||
|
if self.level > 0 {
|
||||||
|
self.add_indent();
|
||||||
|
self.output += "}\n";
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> serde::ser::SerializeMap for &'a mut Serializer {
|
||||||
type Ok = ();
|
type Ok = ();
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
|
@ -465,7 +428,7 @@ where
|
||||||
where
|
where
|
||||||
T: Serialize,
|
T: Serialize,
|
||||||
{
|
{
|
||||||
self.add_indent()?;
|
self.add_indent();
|
||||||
key.serialize(&mut **self)
|
key.serialize(&mut **self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -476,25 +439,25 @@ where
|
||||||
// It doesn't make a difference where the `=` is added. But doing it here
|
// It doesn't make a difference where the `=` is added. But doing it here
|
||||||
// means `serialize_key` is only a call to a different function, which should
|
// means `serialize_key` is only a call to a different function, which should
|
||||||
// have greater optimization potential for the compiler.
|
// have greater optimization potential for the compiler.
|
||||||
self.write(b" = ")?;
|
self.output += " = ";
|
||||||
value.serialize(&mut **self)?;
|
value.serialize(&mut **self)?;
|
||||||
self.write(b"\n")
|
if !self.output.ends_with('\n') {
|
||||||
|
self.output += "\n";
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn end(self) -> Result<Self::Ok> {
|
fn end(self) -> Result<Self::Ok> {
|
||||||
if self.level > 1 {
|
if self.level > 1 {
|
||||||
self.level -= 1;
|
self.level -= 1;
|
||||||
self.add_indent()?;
|
self.add_indent();
|
||||||
self.write(b"}")?;
|
self.output += "}\n";
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, W> serde::ser::SerializeStruct for &'a mut Serializer<W>
|
impl<'a> serde::ser::SerializeStruct for &'a mut Serializer {
|
||||||
where
|
|
||||||
W: io::Write,
|
|
||||||
{
|
|
||||||
type Ok = ();
|
type Ok = ();
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
|
@ -502,29 +465,29 @@ where
|
||||||
where
|
where
|
||||||
T: Serialize,
|
T: Serialize,
|
||||||
{
|
{
|
||||||
self.add_indent()?;
|
self.add_indent();
|
||||||
key.serialize(&mut **self)?;
|
key.serialize(&mut **self)?;
|
||||||
|
|
||||||
self.write(b" = ")?;
|
self.output += " = ";
|
||||||
|
|
||||||
value.serialize(&mut **self)?;
|
value.serialize(&mut **self)?;
|
||||||
self.write(b"\n")
|
if !self.output.ends_with('\n') {
|
||||||
|
self.output += "\n";
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn end(self) -> Result<Self::Ok> {
|
fn end(self) -> Result<Self::Ok> {
|
||||||
if self.level > 1 {
|
if self.level > 1 {
|
||||||
self.level -= 1;
|
self.level -= 1;
|
||||||
self.add_indent()?;
|
self.add_indent();
|
||||||
self.write(b"}")?;
|
self.output += "}\n";
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, W> serde::ser::SerializeStructVariant for &'a mut Serializer<W>
|
impl<'a> serde::ser::SerializeStructVariant for &'a mut Serializer {
|
||||||
where
|
|
||||||
W: std::io::Write,
|
|
||||||
{
|
|
||||||
type Ok = ();
|
type Ok = ();
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
|
@ -532,18 +495,21 @@ where
|
||||||
where
|
where
|
||||||
T: Serialize,
|
T: Serialize,
|
||||||
{
|
{
|
||||||
self.add_indent()?;
|
self.add_indent();
|
||||||
key.serialize(&mut **self)?;
|
key.serialize(&mut **self)?;
|
||||||
self.write(b" = ")?;
|
self.output += " = ";
|
||||||
value.serialize(&mut **self)?;
|
value.serialize(&mut **self)?;
|
||||||
self.write(b"\n")
|
if !self.output.ends_with('\n') {
|
||||||
|
self.output += "\n";
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn end(self) -> Result<Self::Ok> {
|
fn end(self) -> Result<Self::Ok> {
|
||||||
if self.level > 0 {
|
if self.level > 0 {
|
||||||
self.level -= 1;
|
self.level -= 1;
|
||||||
self.add_indent()?;
|
self.add_indent();
|
||||||
self.write(b"}")?;
|
self.output += "}\n";
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ fn serialize_u64() {
|
||||||
|
|
||||||
let tests = [u64::MIN, 4, u64::MAX];
|
let tests = [u64::MIN, 4, u64::MAX];
|
||||||
for value in tests {
|
for value in tests {
|
||||||
let expected = format!("value = {value}\n");
|
let expected = format!("value = {}\n", value);
|
||||||
let value = Value { value };
|
let value = Value { value };
|
||||||
let actual = to_string(&value).unwrap();
|
let actual = to_string(&value).unwrap();
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
|
@ -38,7 +38,7 @@ fn serialize_i64() {
|
||||||
|
|
||||||
let tests = [i64::MIN, -6, 0, 4, i64::MAX];
|
let tests = [i64::MIN, -6, 0, 4, i64::MAX];
|
||||||
for value in tests {
|
for value in tests {
|
||||||
let expected = format!("value = {value}\n");
|
let expected = format!("value = {}\n", value);
|
||||||
let value = Value { value };
|
let value = Value { value };
|
||||||
let actual = to_string(&value).unwrap();
|
let actual = to_string(&value).unwrap();
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
|
@ -61,7 +61,7 @@ fn serialize_f64() {
|
||||||
std::f64::consts::PI,
|
std::f64::consts::PI,
|
||||||
];
|
];
|
||||||
for value in tests {
|
for value in tests {
|
||||||
let expected = format!("value = {value}\n");
|
let expected = format!("value = {}\n", value);
|
||||||
let value = Value { value };
|
let value = Value { value };
|
||||||
let actual = to_string(&value).unwrap();
|
let actual = to_string(&value).unwrap();
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
|
@ -81,14 +81,17 @@ fn serialize_non_representable_floats() {
|
||||||
}
|
}
|
||||||
|
|
||||||
let tests = [std::f64::NAN, std::f64::INFINITY, std::f64::NEG_INFINITY];
|
let tests = [std::f64::NAN, std::f64::INFINITY, std::f64::NEG_INFINITY];
|
||||||
|
let expected = String::from("value = null\n");
|
||||||
for value in tests {
|
for value in tests {
|
||||||
let value = Value64 { value };
|
let value = Value64 { value };
|
||||||
assert!(to_string(&value).is_err());
|
let actual = to_string(&value).unwrap();
|
||||||
|
assert_eq!(actual, expected);
|
||||||
}
|
}
|
||||||
let tests = [std::f32::NAN, std::f32::INFINITY, std::f32::NEG_INFINITY];
|
let tests = [std::f32::NAN, std::f32::INFINITY, std::f32::NEG_INFINITY];
|
||||||
for value in tests {
|
for value in tests {
|
||||||
let value = Value32 { value };
|
let value = Value32 { value };
|
||||||
assert!(to_string(&value).is_err());
|
let actual = to_string(&value).unwrap();
|
||||||
|
assert_eq!(actual, expected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +104,7 @@ fn serialize_bool() {
|
||||||
|
|
||||||
let tests = [true, false];
|
let tests = [true, false];
|
||||||
for value in tests {
|
for value in tests {
|
||||||
let expected = format!("value = {value}\n");
|
let expected = format!("value = {}\n", value);
|
||||||
let value = Value { value };
|
let value = Value { value };
|
||||||
let actual = to_string(&value).unwrap();
|
let actual = to_string(&value).unwrap();
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
|
@ -122,18 +125,11 @@ fn serialize_string() {
|
||||||
("foo\nbar", "\"foo\\nbar\""),
|
("foo\nbar", "\"foo\\nbar\""),
|
||||||
("foo\r\nbar", "\"foo\\r\\nbar\""),
|
("foo\r\nbar", "\"foo\\r\\nbar\""),
|
||||||
("foo\tbar", "\"foo\\tbar\""),
|
("foo\tbar", "\"foo\\tbar\""),
|
||||||
("foo/bar", "foo/bar"),
|
("foo/bar", "\"foo\\/bar\""),
|
||||||
("foo\\bar", "\"foo\\\\bar\""),
|
("foo\\bar", "\"foo\\\\bar\""),
|
||||||
// Regression test for #7.
|
|
||||||
("scripts/mods/test\\new", "\"scripts/mods/test\\\\new\""),
|
|
||||||
// Regression test for #8.
|
|
||||||
(
|
|
||||||
"+002023-03-03T16:42:33.944311860Z",
|
|
||||||
"\"+002023-03-03T16:42:33.944311860Z\"",
|
|
||||||
),
|
|
||||||
];
|
];
|
||||||
for (value, expected) in tests {
|
for (value, expected) in tests {
|
||||||
let expected = format!("value = {expected}\n");
|
let expected = format!("value = {}\n", expected);
|
||||||
let value = Value {
|
let value = Value {
|
||||||
value: value.to_string(),
|
value: value.to_string(),
|
||||||
};
|
};
|
||||||
|
@ -156,12 +152,12 @@ fn serialize_char() {
|
||||||
('\t', "\"\\t\""),
|
('\t', "\"\\t\""),
|
||||||
('\r', "\"\\r\""),
|
('\r', "\"\\r\""),
|
||||||
('\\', "\"\\\\\""),
|
('\\', "\"\\\\\""),
|
||||||
('/', "/"),
|
('/', "\"\\/\""),
|
||||||
('\"', "\"\\\"\""),
|
('\"', "\"\\\"\""),
|
||||||
('\'', "\"'\""),
|
('\'', "\"'\""),
|
||||||
];
|
];
|
||||||
for (value, expected) in tests {
|
for (value, expected) in tests {
|
||||||
let expected = format!("value = {expected}\n");
|
let expected = format!("value = {}\n", expected);
|
||||||
let value = Value {
|
let value = Value {
|
||||||
value: value.to_string(),
|
value: value.to_string(),
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Reference in a new issue