1
Fork 0

Compare commits

...

27 commits

Author SHA1 Message Date
a5117b272d
chore: Release serde_sjson version 1.2.0 2024-03-21 13:52:57 +01:00
3a090a8036
Merge branch 'feat/prepare-publish'
* feat/prepare-publish:
  Add publishing command
  Improve package metadata
  Add documentation
2024-03-21 13:52:02 +01:00
1b3d16c479
Add publishing command 2024-03-21 13:48:51 +01:00
5a2855f0ae
Improve package metadata 2024-03-21 13:39:52 +01:00
8c5f0ad7d1
Add documentation 2024-03-21 13:39:41 +01:00
f0ee27779d
Release v1.1.0 2024-03-21 10:20:17 +01:00
609c711e3f
Merge pull request 'Implement generic writer target' (#12) from feat/to-writer into master
* feat/to-writer:
  Implement generic writer target
2024-03-21 10:17:29 +01:00
5ec97dab43
Implement generic writer target
Implements `to_writer` and `to_vec` functions.
2024-03-21 10:13:45 +01:00
73d2b23ce5 Merge pull request 'fix(parser): Fix parsing CRLF' (#11) from issue/10 into master
Reviewed-on: #11
2023-03-15 21:31:06 +01:00
66e708c9e6
fix(parser): Fix parsing CRLF 2023-03-15 21:30:19 +01:00
6e413b7bf5
version: v1.0.0 2023-03-10 11:27:59 +01:00
6d052fdd66
chore: Update crates 2023-03-10 11:26:26 +01:00
702b2e4411
fix(ser): Fix serializing certain escaped characters
Fixes #7.
Closes #9.
2023-03-10 10:59:03 +01:00
516ddd1b08
feat: Implement literal strings
Closes #5.
2023-03-10 10:42:54 +01:00
81213f7927
fix: Fix serializing strings containing :
Fixes #8.
2023-03-03 17:51:54 +01:00
e94218d8f5
version: v0.2.4 2023-03-01 19:48:08 +01:00
1ca19b4dda
fix(parser): Fix incorrect parsing of unquoted strings
Closes #4.
2023-03-01 19:31:18 +01:00
a6ef5a914e
version: v0.2.3 2023-02-24 11:27:54 +01:00
25b4083379
Merge branch 'fix/backslash'
* fix/backslash:
  fix(parser): Support backslashes in delimited strings
2023-02-24 11:26:27 +01:00
81896339a3
fix(parser): Support backslashes in delimited strings
Fixes #2.
2023-02-24 11:15:17 +01:00
fc5d8b25fb
version: v0.2.2 2023-02-18 18:37:37 +01:00
5a367bc478
fix: Fix deserializing arrays and structs
I'm still not sure when `deserialize_any` is called over one of the
specific variants, but it seems like I do have do make `deserialize_any`
aware of all value types.

Fixes #1.
2023-02-18 18:28:44 +01:00
39486e8503
version: v0.2.1 2022-12-28 19:48:42 +01:00
f76acf5407
fix: Fix serializing Unicode
The index operator doesn't use the `char` boundaries, but rather
byte boundaries. So I switched back to a simpler, but slightly
less efficient loop that simply adds individual characters to the
output.

It also doesn't escape Unicode anymore, as this shouldn't be an issue
in UTF-8 encoded output files.
2022-12-28 19:48:01 +01:00
ded56befb2
refactor: Apply clippy suggestions 2022-12-08 22:31:30 +01:00
e037ef7659
version: v0.2.0 2022-11-25 16:09:53 +01:00
d4ea35d273
feat: Implement deserialization 2022-11-25 16:08:58 +01:00
14 changed files with 1635 additions and 306 deletions

View file

@ -1,14 +0,0 @@
= Changelog
:toc:
:toclevels: 1
:idprefix:
:idseparator: -
== [Unreleased]
== [v0.1.0] - 2022-11-18
=== Added
* initial release
* serialization

69
CHANGELOG.md Normal file
View file

@ -0,0 +1,69 @@
# 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

View file

@ -1,13 +1,27 @@
[package]
name = "serde_sjson"
version = "0.1.0"
version = "1.2.0"
authors = ["Lucas Schwiderski"]
categories = ["encoding", "parser-implementations"]
description = "An SJSON serialization file format"
documentation = "https://docs.rs/serde_sjson"
edition = "2021"
keywords = ["serde", "serialization", "sjson"]
description = "An SJSON serialization file format"
categories = ["encoding", "parser-implementations"]
license-file = "LICENSE"
repository = "https://github.com/sclu1034/serde_sjson"
exclude = [
".github/",
".ci/",
"Justfile"
]
[dependencies]
serde = { version = "1.0.147", default-features = false }
nom = "7"
nom_locate = "4.1"
serde = { version = "1.0", default-features = false }
[dev-dependencies]
serde = { version = "1.0.147", features = ["derive"] }
serde = { version = "1.0.194", features = ["derive"] }
[badges]
maintenance = { status = "passively-maintained" }

View file

@ -1,12 +1,26 @@
project := "serde_sjson"
default := "run"
build *ARGS:
cargo build {{ARGS}}
cargo readme > README.md
run *ARGS:
cargo run -- {{ARGS}}
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:
RUSTFLAGS="-C instrument-coverage" cargo test --tests {{ARGS}} || true
cargo profdata -- merge -sparse default*.profraw -o {{project}}.profdata

View file

@ -1,13 +0,0 @@
= 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 Normal file
View file

@ -0,0 +1,69 @@
# 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(())
}
```

3
README.tpl Normal file
View file

@ -0,0 +1,3 @@
# {{crate}}
{{readme}}

5
release.toml Normal file
View file

@ -0,0 +1,5 @@
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},
]

747
src/de.rs
View file

@ -1,233 +1,798 @@
pub struct Deserializer {}
use nom::IResult;
use serde::de::{EnumAccess, IntoDeserializer, VariantAccess};
use serde::Deserialize;
impl serde::Deserializer for Deserializer {
type Error;
use crate::error::{Error, ErrorCode, Result};
use crate::parser::*;
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: serde::de::Visitor<'de>,
{
todo!()
/// A container for deserializing Rust values from SJSON.
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 deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: serde::de::Visitor<'de>,
{
todo!()
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 deserialize_i8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: serde::de::Visitor<'de>,
{
todo!()
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 deserialize_i16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: serde::de::Visitor<'de>,
{
todo!()
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 deserialize_i32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: serde::de::Visitor<'de>,
{
todo!()
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 deserialize_i64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
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
V: serde::de::Visitor<'de>,
{
todo!()
if self.is_top_level {
return Err(self.error(ErrorCode::ExpectedTopLevelObject));
}
match self.peek_token()? {
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_u8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value>
where
V: serde::de::Visitor<'de>,
{
todo!()
if self.is_top_level {
return Err(self.error(ErrorCode::ExpectedTopLevelObject));
}
if let Ok(Token::Boolean(val)) = self.parse(&parse_bool) {
visitor.visit_bool(val)
} else {
Err(self.error(ErrorCode::ExpectedBoolean))
}
}
fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value>
where
V: serde::de::Visitor<'de>,
{
todo!()
self.deserialize_i64(visitor)
}
fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value>
where
V: serde::de::Visitor<'de>,
{
todo!()
self.deserialize_i64(visitor)
}
fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value>
where
V: serde::de::Visitor<'de>,
{
todo!()
self.deserialize_i64(visitor)
}
fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value>
where
V: serde::de::Visitor<'de>,
{
todo!()
if self.is_top_level {
return Err(self.error(ErrorCode::ExpectedTopLevelObject));
}
if let Ok(Token::Integer(val)) = self.parse(&parse_integer) {
visitor.visit_i64(val)
} else {
Err(self.error(ErrorCode::ExpectedInteger))
}
}
fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value>
where
V: serde::de::Visitor<'de>,
{
todo!()
self.deserialize_i64(visitor)
}
fn deserialize_char<V>(self, visitor: V) -> Result<V::Value, Self::Error>
fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value>
where
V: serde::de::Visitor<'de>,
{
todo!()
self.deserialize_i64(visitor)
}
fn deserialize_str<V>(self, visitor: V) -> Result<V::Value, Self::Error>
fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value>
where
V: serde::de::Visitor<'de>,
{
todo!()
self.deserialize_i64(visitor)
}
fn deserialize_string<V>(self, visitor: V) -> Result<V::Value, Self::Error>
fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value>
where
V: serde::de::Visitor<'de>,
{
todo!()
self.deserialize_i64(visitor)
}
fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value, Self::Error>
fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value>
where
V: serde::de::Visitor<'de>,
{
todo!()
self.deserialize_f64(visitor)
}
fn deserialize_byte_buf<V>(self, visitor: V) -> Result<V::Value, Self::Error>
fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value>
where
V: serde::de::Visitor<'de>,
{
todo!()
if self.is_top_level {
return Err(self.error(ErrorCode::ExpectedTopLevelObject));
}
if let Ok(Token::Float(val)) = self.parse(&parse_float) {
visitor.visit_f64(val)
} else {
Err(self.error(ErrorCode::ExpectedFloat))
}
}
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
fn deserialize_char<V>(self, visitor: V) -> Result<V::Value>
where
V: serde::de::Visitor<'de>,
{
todo!()
self.deserialize_str(visitor)
}
fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value, Self::Error>
fn deserialize_str<V>(self, visitor: V) -> Result<V::Value>
where
V: serde::de::Visitor<'de>,
{
todo!()
if self.is_top_level {
return Err(self.error(ErrorCode::ExpectedTopLevelObject));
}
if let Ok(Token::String(val)) = self.parse(&parse_string) {
visitor.visit_str(&val)
} else {
Err(self.error(ErrorCode::ExpectedString))
}
}
fn deserialize_unit_struct<V>(
self,
name: &'static str,
visitor: V,
) -> Result<V::Value, Self::Error>
fn deserialize_string<V>(self, visitor: V) -> Result<V::Value>
where
V: serde::de::Visitor<'de>,
{
todo!()
self.deserialize_str(visitor)
}
fn deserialize_newtype_struct<V>(
self,
name: &'static str,
visitor: V,
) -> Result<V::Value, Self::Error>
fn deserialize_bytes<V>(self, _visitor: V) -> Result<V::Value>
where
V: serde::de::Visitor<'de>,
{
todo!()
unimplemented!()
}
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error>
fn deserialize_byte_buf<V>(self, _visitor: V) -> Result<V::Value>
where
V: serde::de::Visitor<'de>,
{
todo!()
unimplemented!()
}
fn deserialize_tuple<V>(self, len: usize, visitor: V) -> Result<V::Value, Self::Error>
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value>
where
V: serde::de::Visitor<'de>,
{
todo!()
if self.is_top_level {
return Err(self.error(ErrorCode::ExpectedTopLevelObject));
}
if self.peek_token()? == Token::Null {
let _ = self.next_token()?;
visitor.visit_none()
} else {
visitor.visit_some(self)
}
}
fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value>
where
V: serde::de::Visitor<'de>,
{
if self.is_top_level {
return Err(self.error(ErrorCode::ExpectedTopLevelObject));
}
if let Ok(Token::Null) = self.parse(&parse_null) {
visitor.visit_unit()
} else {
Err(self.error(ErrorCode::ExpectedNull))
}
}
fn deserialize_unit_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value>
where
V: serde::de::Visitor<'de>,
{
self.deserialize_unit(visitor)
}
fn deserialize_newtype_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value>
where
V: serde::de::Visitor<'de>,
{
visitor.visit_newtype_struct(self)
}
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value>
where
V: serde::de::Visitor<'de>,
{
if self.is_top_level {
return Err(self.error(ErrorCode::ExpectedTopLevelObject));
}
if self.next_token()? != Token::ArrayStart {
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
V: serde::de::Visitor<'de>,
{
self.deserialize_seq(visitor)
}
fn deserialize_tuple_struct<V>(
self,
name: &'static str,
len: usize,
_name: &'static str,
_len: usize,
visitor: V,
) -> Result<V::Value, Self::Error>
) -> Result<V::Value>
where
V: serde::de::Visitor<'de>,
{
todo!()
self.deserialize_seq(visitor)
}
fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Self::Error>
fn deserialize_map<V>(self, visitor: V) -> Result<V::Value>
where
V: serde::de::Visitor<'de>,
{
todo!()
if self.is_top_level {
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>(
self,
name: &'static str,
fields: &'static [&'static str],
_name: &'static str,
_fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error>
) -> Result<V::Value>
where
V: serde::de::Visitor<'de>,
{
todo!()
self.deserialize_map(visitor)
}
fn deserialize_enum<V>(
self,
name: &'static str,
variants: &'static [&'static str],
_name: &'static str,
_variants: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error>
) -> Result<V::Value>
where
V: serde::de::Visitor<'de>,
{
todo!()
match self.next_token()? {
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, Self::Error>
fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value>
where
V: serde::de::Visitor<'de>,
{
todo!()
if let Ok(Token::String(val)) = self.parse(&parse_identifier) {
visitor.visit_str(&val)
} else {
Err(self.error(ErrorCode::ExpectedString))
}
}
fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value>
where
V: serde::de::Visitor<'de>,
{
todo!()
self.deserialize_any(visitor)
}
}
pub fn from_str<T>() -> crate::Result<T> {}
struct Separated<'a, 'de: 'a> {
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);
}
}

View file

@ -1,57 +1,74 @@
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>;
/// A type encapsulating the different errors that might occurr
/// during serialization or deserialization.
#[derive(PartialEq)]
pub struct Error {
inner: Box<ErrorImpl>,
}
#[derive(PartialEq)]
struct ErrorImpl {
code: ErrorCode,
line: usize,
line: u32,
column: usize,
fragment: Option<String>,
token: Option<Token>,
}
// TODO: Remove once they are constructed
#[allow(dead_code)]
#[derive(PartialEq)]
pub(crate) enum ErrorCode {
// Generic error built from a message or different error
Message(String),
// Wrap inner I/O errors
Io(io::Error),
Eof,
Syntax,
ExpectedTopLevelObject,
ExpectedBoolean,
ExpectedInteger,
ExpectedString,
ExpectedNull,
ExpectedArray,
ExpectedArrayEnd,
ExpectedArraySeparator,
ExpectedBoolean,
ExpectedEnum,
ExpectedFloat,
ExpectedInteger,
ExpectedMap,
ExpectedMapEquals,
ExpectedMapEnd,
ExpectedMapEquals,
ExpectedMapSeparator,
ExpectedNull,
ExpectedString,
ExpectedTopLevelObject,
ExpectedValue,
TrailingCharacters,
NonFiniteFloat,
}
impl fmt::Display for ErrorCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
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::ExpectedArrayEnd => f.write_str("expected an array end delimiter"),
ErrorCode::ExpectedMap => f.write_str("expected an object value"),
ErrorCode::ExpectedMapEquals => f.write_str("expected a '=' between key and value"),
ErrorCode::ExpectedArraySeparator => {
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::ExpectedMapSeparator => {
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::NonFiniteFloat => f.write_str("got infinite floating point number"),
}
}
}
@ -80,10 +97,12 @@ impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Error({:?}, line: {}, column: {})",
"Error({:?}, line: {}, column: {}, fragment: {:?}, token: {:?})",
self.inner.code.to_string(),
self.inner.line,
self.inner.column
self.inner.column,
self.inner.fragment,
self.inner.token,
)
}
}
@ -97,6 +116,8 @@ impl serde::de::Error for Error {
code: ErrorCode::Message(msg.to_string()),
line: 0,
column: 0,
fragment: None,
token: None,
});
Self { inner }
}
@ -111,6 +132,8 @@ impl serde::ser::Error for Error {
code: ErrorCode::Message(msg.to_string()),
line: 0,
column: 0,
fragment: None,
token: None,
});
Self { inner }
}
@ -119,9 +142,38 @@ impl serde::ser::Error for Error {
impl std::error::Error for Error {}
impl Error {
pub(crate) fn new(code: ErrorCode, line: usize, column: usize) -> Self {
pub(crate) fn new(code: ErrorCode, line: u32, column: usize, fragment: Option<String>) -> Self {
Self {
inner: Box::new(ErrorImpl { code, line, column }),
inner: Box::new(ErrorImpl {
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)
}
}

View file

@ -1,7 +1,76 @@
// mod de;
//! A **ser**ialization/**de**serialization library for Simplified JSON,
//! 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 parser;
mod ser;
// pub use de::{from_str, Deserializer};
pub use de::{from_str, Deserializer};
pub use error::{Error, Result};
pub use ser::{to_string, Serializer};
pub use ser::{to_string, to_vec, to_writer, Serializer};

458
src/parser.rs Normal file
View file

@ -0,0 +1,458 @@
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),
],
);
}
}

View file

@ -1,46 +1,93 @@
use std::io;
use serde::Serialize;
use crate::error::{Error, ErrorCode, Result};
// TODO: Make configurable
const INDENT: &str = " ";
const INDENT: [u8; 2] = [0x20, 0x20];
pub struct Serializer {
/// A container for serializing Rust values into SJSON.
pub struct Serializer<W> {
// The current indentation level
level: usize,
// The output string
output: String,
writer: W,
}
/// 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>
where
T: Serialize,
{
let mut serializer = Serializer {
level: 0,
output: String::new(),
let vec = to_vec(value)?;
let string = if cfg!(debug_assertions) {
String::from_utf8(vec).expect("We do not emit invalid UTF-8")
} else {
unsafe { String::from_utf8_unchecked(vec) }
};
value.serialize(&mut serializer)?;
Ok(serializer.output)
Ok(string)
}
impl Serializer {
fn add_indent(&mut self) {
for _ in 0..self.level.saturating_sub(1) {
self.output += INDENT;
}
impl<W> Serializer<W>
where
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) {
self.write(INDENT)?;
}
Ok(())
}
#[inline]
fn ensure_top_level_struct(&self) -> Result<()> {
if self.level == 0 {
return Err(Error::new(ErrorCode::ExpectedTopLevelObject, 0, 0));
return Err(Error::new(ErrorCode::ExpectedTopLevelObject, 0, 0, None));
}
Ok(())
}
}
impl<'a> serde::ser::Serializer for &'a mut Serializer {
impl<'a, W> serde::ser::Serializer for &'a mut Serializer<W>
where
W: io::Write,
{
type Ok = ();
type Error = Error;
@ -54,7 +101,7 @@ impl<'a> serde::ser::Serializer for &'a mut Serializer {
fn serialize_bool(self, v: bool) -> Result<Self::Ok> {
self.ensure_top_level_struct()?;
self.output += if v { "true" } else { "false" };
self.write(if v { "true" } else { "false" })?;
Ok(())
}
@ -72,8 +119,7 @@ impl<'a> serde::ser::Serializer for &'a mut Serializer {
fn serialize_i64(self, v: i64) -> Result<Self::Ok> {
self.ensure_top_level_struct()?;
self.output += &v.to_string();
Ok(())
self.serialize_str(&format!("{}", v))
}
fn serialize_u8(self, v: u8) -> Result<Self::Ok> {
@ -90,90 +136,80 @@ impl<'a> serde::ser::Serializer for &'a mut Serializer {
fn serialize_u64(self, v: u64) -> Result<Self::Ok> {
self.ensure_top_level_struct()?;
self.output += &v.to_string();
Ok(())
self.serialize_str(&format!("{}", v))
}
fn serialize_f32(self, v: f32) -> Result<Self::Ok> {
if v.is_finite() {
self.serialize_f64(v.into())
} else {
self.ensure_top_level_struct()?;
self.output += "null";
Ok(())
}
self.serialize_f64(v.into())
}
fn serialize_f64(self, v: f64) -> Result<Self::Ok> {
self.ensure_top_level_struct()?;
if v.is_finite() {
self.output += &v.to_string();
} else {
self.output += "null";
if !v.is_finite() {
return Err(Error::new(ErrorCode::NonFiniteFloat, 0, 0, None));
}
Ok(())
self.serialize_str(&format!("{}", v))
}
fn serialize_char(self, v: char) -> Result<Self::Ok> {
let mut buf = [0; 4];
self.serialize_str(v.encode_utf8(&mut buf))
self.serialize_bytes(v.encode_utf8(&mut buf).as_bytes())
}
fn serialize_str(self, v: &str) -> Result<Self::Ok> {
self.ensure_top_level_struct()?;
let needs_escapes =
v.is_empty() || v.contains([' ', '\n', '\r', '\t', '=', '\'', '"', '\\', '/']);
if needs_escapes {
self.output += "\"";
let len = v.len();
let chars = v.chars();
let mut start = 0;
let needs_quotes =
v.is_empty() || v.contains([' ', '\n', '\r', '\t', '=', '\'', '"', '\\', ':']);
for (i, c) in chars.enumerate() {
if ('\x20'..='\x7e').contains(&c)
&& !['\t', '\n', '\r', '\"', '\\', '/'].contains(&c)
{
continue;
}
self.output += &v[start..i];
self.output.push('\\');
if needs_quotes {
self.write(b"\"")?;
// Since we've added a layer of quotes, we now need to escape
// certain characters.
for c in v.chars() {
match c {
'\t' => {
self.output.push('t');
self.write(b"\\")?;
self.write(b"t")?;
}
'\n' => {
self.output.push('n');
self.write(b"\\")?;
self.write(b"n")?;
}
'\r' => {
self.output.push('r');
self.write(b"\\")?;
self.write(b"r")?;
}
'\x7f'.. => {
self.output += &format!("u{:4x}", c as u32);
'"' => {
self.write(b"\\")?;
self.write(b"\"")?;
}
'\\' => {
self.write(b"\\")?;
self.write(b"\\")?;
}
c => {
self.output.push(c);
self.serialize_char(c)?;
}
};
start = i + 1;
}
if start < len {
self.output += &v[start..];
}
self.output += "\"";
self.write(b"\"")?;
} else {
self.output += v;
self.write(v.as_bytes())?;
}
Ok(())
}
fn serialize_bytes(self, _v: &[u8]) -> Result<Self::Ok> {
todo!()
fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok> {
self.ensure_top_level_struct()?;
// 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> {
@ -193,8 +229,7 @@ impl<'a> serde::ser::Serializer for &'a mut Serializer {
fn serialize_unit(self) -> Result<Self::Ok> {
self.ensure_top_level_struct()?;
self.output += "null";
Ok(())
self.write(b"null")
}
fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok> {
@ -232,19 +267,18 @@ impl<'a> serde::ser::Serializer for &'a mut Serializer {
{
self.ensure_top_level_struct()?;
self.output += "{ ";
self.write(b"{ ")?;
variant.serialize(&mut *self)?;
self.output += " = ";
self.write(b" = ")?;
value.serialize(&mut *self)?;
self.output += " }\n";
Ok(())
self.write(b" }")
}
// Serialize the start of a sequence.
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
self.ensure_top_level_struct()?;
self.output += "[\n";
self.write(b"[\n")?;
self.level += 1;
Ok(self)
}
@ -275,7 +309,7 @@ impl<'a> serde::ser::Serializer for &'a mut Serializer {
variant.serialize(&mut *self)?;
self.output += " = [\n";
self.write(b" = [\n")?;
self.level += 1;
Ok(self)
@ -283,7 +317,7 @@ impl<'a> serde::ser::Serializer for &'a mut Serializer {
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
if self.level > 0 {
self.output += "{\n";
self.write(b"{\n")?;
}
self.level += 1;
Ok(self)
@ -305,7 +339,7 @@ impl<'a> serde::ser::Serializer for &'a mut Serializer {
variant.serialize(&mut *self)?;
self.output += " = {\n";
self.write(b" = {\n")?;
self.level += 1;
Ok(self)
@ -319,7 +353,10 @@ impl<'a> serde::ser::Serializer for &'a mut Serializer {
}
}
impl<'a> serde::ser::SerializeSeq for &'a mut Serializer {
impl<'a, W> serde::ser::SerializeSeq for &'a mut Serializer<W>
where
W: io::Write,
{
type Ok = ();
type Error = Error;
@ -327,23 +364,22 @@ impl<'a> serde::ser::SerializeSeq for &'a mut Serializer {
where
T: Serialize,
{
self.add_indent();
self.add_indent()?;
value.serialize(&mut **self)?;
if !self.output.ends_with('\n') {
self.output += "\n";
}
Ok(())
self.write(b"\n")
}
fn end(self) -> Result<Self::Ok> {
self.level -= 1;
self.add_indent();
self.output += "]\n";
Ok(())
self.add_indent()?;
self.write(b"]")
}
}
impl<'a> serde::ser::SerializeTuple for &'a mut Serializer {
impl<'a, W> serde::ser::SerializeTuple for &'a mut Serializer<W>
where
W: io::Write,
{
type Ok = ();
type Error = Error;
@ -351,23 +387,22 @@ impl<'a> serde::ser::SerializeTuple for &'a mut Serializer {
where
T: Serialize,
{
self.add_indent();
self.add_indent()?;
value.serialize(&mut **self)?;
if !self.output.ends_with('\n') {
self.output += "\n";
}
Ok(())
self.write(b"\n")
}
fn end(self) -> Result<Self::Ok> {
self.level -= 1;
self.add_indent();
self.output += "]\n";
Ok(())
self.add_indent()?;
self.write(b"]")
}
}
impl<'a> serde::ser::SerializeTupleStruct for &'a mut Serializer {
impl<'a, W> serde::ser::SerializeTupleStruct for &'a mut Serializer<W>
where
W: io::Write,
{
type Ok = ();
type Error = Error;
@ -375,23 +410,22 @@ impl<'a> serde::ser::SerializeTupleStruct for &'a mut Serializer {
where
T: Serialize,
{
self.add_indent();
self.add_indent()?;
value.serialize(&mut **self)?;
if !self.output.ends_with('\n') {
self.output += "\n";
}
Ok(())
self.write(b"\n")
}
fn end(self) -> Result<Self::Ok> {
self.level -= 1;
self.add_indent();
self.output += "]\n";
Ok(())
self.add_indent()?;
self.write(b"]")
}
}
impl<'a> serde::ser::SerializeTupleVariant for &'a mut Serializer {
impl<'a, W> serde::ser::SerializeTupleVariant for &'a mut Serializer<W>
where
W: io::Write,
{
type Ok = ();
type Error = Error;
@ -399,28 +433,31 @@ impl<'a> serde::ser::SerializeTupleVariant for &'a mut Serializer {
where
T: Serialize,
{
self.add_indent();
self.add_indent()?;
value.serialize(&mut **self)?;
if !self.output.ends_with('\n') {
self.output += "\n";
}
Ok(())
self.write(b"\n")
}
fn end(self) -> Result<Self::Ok> {
self.level -= 1;
self.add_indent();
self.output += "]\n";
self.add_indent()?;
self.write(b"]\n")?;
self.level -= 1;
if self.level > 0 {
self.add_indent();
self.output += "}\n";
self.add_indent()?;
self.write(b"}")?;
}
Ok(())
}
}
impl<'a> serde::ser::SerializeMap for &'a mut Serializer {
impl<'a, W> serde::ser::SerializeMap for &'a mut Serializer<W>
where
W: io::Write,
{
type Ok = ();
type Error = Error;
@ -428,7 +465,7 @@ impl<'a> serde::ser::SerializeMap for &'a mut Serializer {
where
T: Serialize,
{
self.add_indent();
self.add_indent()?;
key.serialize(&mut **self)
}
@ -439,25 +476,25 @@ impl<'a> serde::ser::SerializeMap for &'a mut Serializer {
// 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
// have greater optimization potential for the compiler.
self.output += " = ";
self.write(b" = ")?;
value.serialize(&mut **self)?;
if !self.output.ends_with('\n') {
self.output += "\n";
}
Ok(())
self.write(b"\n")
}
fn end(self) -> Result<Self::Ok> {
if self.level > 1 {
self.level -= 1;
self.add_indent();
self.output += "}\n";
self.add_indent()?;
self.write(b"}")?;
}
Ok(())
}
}
impl<'a> serde::ser::SerializeStruct for &'a mut Serializer {
impl<'a, W> serde::ser::SerializeStruct for &'a mut Serializer<W>
where
W: io::Write,
{
type Ok = ();
type Error = Error;
@ -465,29 +502,29 @@ impl<'a> serde::ser::SerializeStruct for &'a mut Serializer {
where
T: Serialize,
{
self.add_indent();
self.add_indent()?;
key.serialize(&mut **self)?;
self.output += " = ";
self.write(b" = ")?;
value.serialize(&mut **self)?;
if !self.output.ends_with('\n') {
self.output += "\n";
}
Ok(())
self.write(b"\n")
}
fn end(self) -> Result<Self::Ok> {
if self.level > 1 {
self.level -= 1;
self.add_indent();
self.output += "}\n";
self.add_indent()?;
self.write(b"}")?;
}
Ok(())
}
}
impl<'a> serde::ser::SerializeStructVariant for &'a mut Serializer {
impl<'a, W> serde::ser::SerializeStructVariant for &'a mut Serializer<W>
where
W: std::io::Write,
{
type Ok = ();
type Error = Error;
@ -495,21 +532,18 @@ impl<'a> serde::ser::SerializeStructVariant for &'a mut Serializer {
where
T: Serialize,
{
self.add_indent();
self.add_indent()?;
key.serialize(&mut **self)?;
self.output += " = ";
self.write(b" = ")?;
value.serialize(&mut **self)?;
if !self.output.ends_with('\n') {
self.output += "\n";
}
Ok(())
self.write(b"\n")
}
fn end(self) -> Result<Self::Ok> {
if self.level > 0 {
self.level -= 1;
self.add_indent();
self.output += "}\n";
self.add_indent()?;
self.write(b"}")?;
}
Ok(())
}

View file

@ -22,7 +22,7 @@ fn serialize_u64() {
let tests = [u64::MIN, 4, u64::MAX];
for value in tests {
let expected = format!("value = {}\n", value);
let expected = format!("value = {value}\n");
let value = Value { value };
let actual = to_string(&value).unwrap();
assert_eq!(actual, expected);
@ -38,7 +38,7 @@ fn serialize_i64() {
let tests = [i64::MIN, -6, 0, 4, i64::MAX];
for value in tests {
let expected = format!("value = {}\n", value);
let expected = format!("value = {value}\n");
let value = Value { value };
let actual = to_string(&value).unwrap();
assert_eq!(actual, expected);
@ -61,7 +61,7 @@ fn serialize_f64() {
std::f64::consts::PI,
];
for value in tests {
let expected = format!("value = {}\n", value);
let expected = format!("value = {value}\n");
let value = Value { value };
let actual = to_string(&value).unwrap();
assert_eq!(actual, expected);
@ -81,17 +81,14 @@ fn serialize_non_representable_floats() {
}
let tests = [std::f64::NAN, std::f64::INFINITY, std::f64::NEG_INFINITY];
let expected = String::from("value = null\n");
for value in tests {
let value = Value64 { value };
let actual = to_string(&value).unwrap();
assert_eq!(actual, expected);
assert!(to_string(&value).is_err());
}
let tests = [std::f32::NAN, std::f32::INFINITY, std::f32::NEG_INFINITY];
for value in tests {
let value = Value32 { value };
let actual = to_string(&value).unwrap();
assert_eq!(actual, expected);
assert!(to_string(&value).is_err());
}
}
@ -104,7 +101,7 @@ fn serialize_bool() {
let tests = [true, false];
for value in tests {
let expected = format!("value = {}\n", value);
let expected = format!("value = {value}\n");
let value = Value { value };
let actual = to_string(&value).unwrap();
assert_eq!(actual, expected);
@ -125,11 +122,18 @@ fn serialize_string() {
("foo\nbar", "\"foo\\nbar\""),
("foo\r\nbar", "\"foo\\r\\nbar\""),
("foo\tbar", "\"foo\\tbar\""),
("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 {
let expected = format!("value = {}\n", expected);
let expected = format!("value = {expected}\n");
let value = Value {
value: value.to_string(),
};
@ -152,12 +156,12 @@ fn serialize_char() {
('\t', "\"\\t\""),
('\r', "\"\\r\""),
('\\', "\"\\\\\""),
('/', "\"\\/\""),
('/', "/"),
('\"', "\"\\\"\""),
('\'', "\"'\""),
];
for (value, expected) in tests {
let expected = format!("value = {}\n", expected);
let expected = format!("value = {expected}\n");
let value = Value {
value: value.to_string(),
};