Compare commits
1 commit
Author | SHA1 | Date | |
---|---|---|---|
478a085ffe |
15 changed files with 260 additions and 346 deletions
109
.github/workflows/ci.yml
vendored
Normal file
109
.github/workflows/ci.yml
vendored
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
name: Simple build & test
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "master" ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ "master" ]
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
tests:
|
||||||
|
name: ${{ matrix.make.name }} (${{ matrix.os }})
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, macos-latest]
|
||||||
|
rust: [stable, nightly]
|
||||||
|
task:
|
||||||
|
- name: Clippy
|
||||||
|
cmd: "cargo clippy -D warnings"
|
||||||
|
- name: Test
|
||||||
|
cmd: "cargo test"
|
||||||
|
- name: Format
|
||||||
|
cmd: "cargo fmt --all --check"
|
||||||
|
- name: Build
|
||||||
|
cmd: "cargo build --frozen --release"
|
||||||
|
include:
|
||||||
|
- os: ubuntu-latest
|
||||||
|
sccache-path: /home/runner/.cache/sccache
|
||||||
|
- os: macos-latest
|
||||||
|
sccache-path: /Users/runner/Library/Caches/Mozilla.sccache
|
||||||
|
exclude:
|
||||||
|
- os: macos-latest
|
||||||
|
rust: stable
|
||||||
|
task:
|
||||||
|
name: Clippy
|
||||||
|
- os: macos-latest
|
||||||
|
rust: nightly
|
||||||
|
task:
|
||||||
|
name: Clippy
|
||||||
|
- os: macos-latest
|
||||||
|
rust: stable
|
||||||
|
task:
|
||||||
|
name: Format
|
||||||
|
- os: macos-latest
|
||||||
|
rust: nightly
|
||||||
|
task:
|
||||||
|
name: Format
|
||||||
|
env:
|
||||||
|
RUST_BACKTRACE: full
|
||||||
|
RUSTC_WRAPPER: sccache
|
||||||
|
RUSTV: ${{ matrix.rust }}
|
||||||
|
SCCACHE_CACHE_SIZE: 2G
|
||||||
|
SCCACHE_DIR: ${{ matrix.sccache-path }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Install sccache (ubuntu-latest)
|
||||||
|
if: matrix.os == 'ubuntu-latest'
|
||||||
|
env:
|
||||||
|
LINK: https://github.com/mozilla/sccache/releases/download
|
||||||
|
SCCACHE_VERSION: 0.2.13
|
||||||
|
run: |
|
||||||
|
SCCACHE_FILE=sccache-$SCCACHE_VERSION-x86_64-unknown-linux-musl
|
||||||
|
mkdir -p $HOME/.local/bin
|
||||||
|
curl -L "$LINK/$SCCACHE_VERSION/$SCCACHE_FILE.tar.gz" | tar xz
|
||||||
|
mv -f $SCCACHE_FILE/sccache $HOME/.local/bin/sccache
|
||||||
|
echo "$HOME/.local/bin" >> $GITHUB_PATH
|
||||||
|
- name: Install sccache (macos-latest)
|
||||||
|
if: matrix.os == 'macos-latest'
|
||||||
|
run: |
|
||||||
|
brew update
|
||||||
|
brew install sccache
|
||||||
|
- name: Install Rust ${{ matrix.rust }}
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: ${{ matrix.rust }}
|
||||||
|
profile: minimal
|
||||||
|
override: true
|
||||||
|
- name: Cache cargo registry
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cargo/registry
|
||||||
|
~/.cargo/git
|
||||||
|
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-cargo-
|
||||||
|
- name: Save sccache
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: ${{ matrix.sccache-path }}
|
||||||
|
key: ${{ runner.os }}-sccache-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-sccache-
|
||||||
|
- name: Start sccache server
|
||||||
|
run: sccache --start-server
|
||||||
|
- name: Install rustfmt
|
||||||
|
if: matrix.task.name == 'Format'
|
||||||
|
run: rustup component add rustfmt
|
||||||
|
- name: ${{ matrix.task.name }}
|
||||||
|
run: ${{ matrix.task.cmd }}
|
||||||
|
- name: Print sccache stats
|
||||||
|
run: sccache --show-stats
|
||||||
|
- name: Stop sccache server
|
||||||
|
run: sccache --stop-server
|
||||||
|
continue-on-error: true
|
65
CHANGELOG.adoc
Normal file
65
CHANGELOG.adoc
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
= Changelog
|
||||||
|
:toc:
|
||||||
|
:toclevels: 1
|
||||||
|
:idprefix:
|
||||||
|
:idseparator: -
|
||||||
|
|
||||||
|
== [Unreleased]
|
||||||
|
|
||||||
|
== [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
|
75
CHANGELOG.md
75
CHANGELOG.md
|
@ -1,75 +0,0 @@
|
||||||
# Changelog
|
|
||||||
|
|
||||||
<!-- next-header -->
|
|
||||||
|
|
||||||
## [Unreleased] - ReleaseDate
|
|
||||||
|
|
||||||
## [1.2.1] - 2025-04-21
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- update [nom](https://crates.io/crates/nom) to v8
|
|
||||||
|
|
||||||
## [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
|
|
26
Cargo.toml
26
Cargo.toml
|
@ -1,27 +1,15 @@
|
||||||
[package]
|
[package]
|
||||||
name = "serde_sjson"
|
name = "serde_sjson"
|
||||||
version = "1.2.1"
|
version = "1.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 = "8"
|
nom = "7.1.3"
|
||||||
nom_locate = "5"
|
nom_locate = "4.1.0"
|
||||||
serde = { version = "1.0", default-features = false }
|
serde = { version = "1.0.154", default-features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0.154", features = ["derive"] }
|
||||||
|
|
||||||
[badges]
|
|
||||||
maintenance = { status = "passively-maintained" }
|
|
||||||
|
|
21
Justfile
21
Justfile
|
@ -1,33 +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}}
|
||||||
|
|
||||||
check:
|
|
||||||
cargo clippy -- -D warnings
|
|
||||||
cargo test
|
|
||||||
|
|
||||||
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='': check build doc
|
|
||||||
git fetch --all
|
|
||||||
[ "$(git rev-parse master)" = "$(git rev-parse origin/master)" ] \
|
|
||||||
|| (echo "error: master and origin/master differ" >&2; exit 1)
|
|
||||||
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},
|
|
||||||
]
|
|
15
src/de.rs
15
src/de.rs
|
@ -5,7 +5,6 @@ use serde::Deserialize;
|
||||||
use crate::error::{Error, ErrorCode, Result};
|
use crate::error::{Error, ErrorCode, Result};
|
||||||
use crate::parser::*;
|
use crate::parser::*;
|
||||||
|
|
||||||
/// A container for deserializing Rust values from SJSON.
|
|
||||||
pub struct Deserializer<'de> {
|
pub struct Deserializer<'de> {
|
||||||
input: Span<'de>,
|
input: Span<'de>,
|
||||||
is_top_level: bool,
|
is_top_level: bool,
|
||||||
|
@ -13,7 +12,7 @@ pub struct Deserializer<'de> {
|
||||||
|
|
||||||
impl<'de> Deserializer<'de> {
|
impl<'de> Deserializer<'de> {
|
||||||
#![allow(clippy::should_implement_trait)]
|
#![allow(clippy::should_implement_trait)]
|
||||||
pub(crate) fn from_str(input: &'de str) -> Self {
|
pub fn from_str(input: &'de str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
input: Span::from(input),
|
input: Span::from(input),
|
||||||
is_top_level: true,
|
is_top_level: true,
|
||||||
|
@ -66,8 +65,6 @@ impl<'de> Deserializer<'de> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deserializes an SJSON string to a Rust value.
|
|
||||||
#[inline]
|
|
||||||
pub fn from_str<'a, T>(input: &'a str) -> Result<T>
|
pub fn from_str<'a, T>(input: &'a str) -> Result<T>
|
||||||
where
|
where
|
||||||
T: Deserialize<'a>,
|
T: Deserialize<'a>,
|
||||||
|
@ -81,7 +78,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'de> serde::de::Deserializer<'de> for &mut Deserializer<'de> {
|
impl<'de, 'a> serde::de::Deserializer<'de> for &'a mut Deserializer<'de> {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
|
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
|
||||||
|
@ -421,7 +418,7 @@ impl<'a, 'de: 'a> Separated<'a, 'de> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'de> serde::de::SeqAccess<'de> for Separated<'_, 'de> {
|
impl<'de, 'a> serde::de::SeqAccess<'de> for Separated<'a, 'de> {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
|
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
|
||||||
|
@ -443,7 +440,7 @@ impl<'de> serde::de::SeqAccess<'de> for Separated<'_, 'de> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'de> serde::de::MapAccess<'de> for Separated<'_, 'de> {
|
impl<'de, 'a> serde::de::MapAccess<'de> for Separated<'a, 'de> {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>>
|
fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>>
|
||||||
|
@ -487,7 +484,7 @@ impl<'a, 'de> Enum<'a, 'de> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'de> EnumAccess<'de> for Enum<'_, 'de> {
|
impl<'de, 'a> EnumAccess<'de> for Enum<'a, 'de> {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Variant = Self;
|
type Variant = Self;
|
||||||
|
|
||||||
|
@ -505,7 +502,7 @@ impl<'de> EnumAccess<'de> for Enum<'_, 'de> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'de> VariantAccess<'de> for Enum<'_, 'de> {
|
impl<'de, 'a> VariantAccess<'de> for Enum<'a, 'de> {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
fn unit_variant(self) -> Result<()> {
|
fn unit_variant(self) -> Result<()> {
|
||||||
|
|
|
@ -2,11 +2,8 @@ use std::{fmt, io};
|
||||||
|
|
||||||
use crate::parser::Token;
|
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)]
|
#[derive(PartialEq)]
|
||||||
pub struct Error {
|
pub struct Error {
|
||||||
inner: Box<ErrorImpl>,
|
inner: Box<ErrorImpl>,
|
||||||
|
|
68
src/lib.rs
68
src/lib.rs
|
@ -1,71 +1,3 @@
|
||||||
//! 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 de;
|
||||||
mod error;
|
mod error;
|
||||||
mod parser;
|
mod parser;
|
||||||
|
|
|
@ -4,8 +4,8 @@ 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::combinator::{cut, eof, map, map_res, opt, recognize, value};
|
||||||
use nom::multi::many1_count;
|
use nom::multi::many1_count;
|
||||||
use nom::number::complete::double;
|
use nom::number::complete::double;
|
||||||
use nom::sequence::{delimited, preceded, terminated};
|
use nom::sequence::{delimited, preceded, terminated, tuple};
|
||||||
use nom::{IResult, Input as _, Parser as _};
|
use nom::{IResult, Slice};
|
||||||
use nom_locate::LocatedSpan;
|
use nom_locate::LocatedSpan;
|
||||||
|
|
||||||
pub(crate) type Span<'a> = LocatedSpan<&'a str>;
|
pub(crate) type Span<'a> = LocatedSpan<&'a str>;
|
||||||
|
@ -35,25 +35,23 @@ fn whitespace(input: Span) -> IResult<Span, char> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn null(input: Span) -> IResult<Span, ()> {
|
fn null(input: Span) -> IResult<Span, ()> {
|
||||||
value((), tag("null")).parse(input)
|
value((), tag("null"))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn separator(input: Span) -> IResult<Span, &str> {
|
fn separator(input: Span) -> IResult<Span, &str> {
|
||||||
map(alt((tag(","), tag("\n"), tag("\r\n"))), |val: Span| {
|
map(alt((tag(","), tag("\n"), tag("\r\n"))), |val: Span| {
|
||||||
*val.fragment()
|
*val.fragment()
|
||||||
})
|
})(input)
|
||||||
.parse(input)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bool(input: Span) -> IResult<Span, bool> {
|
fn bool(input: Span) -> IResult<Span, bool> {
|
||||||
alt((value(true, tag("true")), value(false, tag("false")))).parse(input)
|
alt((value(true, tag("true")), value(false, tag("false"))))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn integer(input: Span) -> IResult<Span, i64> {
|
fn integer(input: Span) -> IResult<Span, i64> {
|
||||||
map_res(recognize((opt(char('-')), digit1)), |val: Span| {
|
map_res(recognize(tuple((opt(char('-')), digit1))), |val: Span| {
|
||||||
val.fragment().parse::<i64>()
|
val.fragment().parse::<i64>()
|
||||||
})
|
})(input)
|
||||||
.parse(input)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn float(input: Span) -> IResult<Span, f64> {
|
fn float(input: Span) -> IResult<Span, f64> {
|
||||||
|
@ -63,16 +61,14 @@ fn float(input: Span) -> IResult<Span, f64> {
|
||||||
fn identifier(input: Span) -> IResult<Span, &str> {
|
fn identifier(input: Span) -> IResult<Span, &str> {
|
||||||
map(recognize(many1_count(none_of("\" \t\n=:"))), |val: Span| {
|
map(recognize(many1_count(none_of("\" \t\n=:"))), |val: Span| {
|
||||||
*val.fragment()
|
*val.fragment()
|
||||||
})
|
})(input)
|
||||||
.parse(input)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn literal_string(input: Span) -> IResult<Span, &str> {
|
fn literal_string(input: Span) -> IResult<Span, &str> {
|
||||||
map(
|
map(
|
||||||
delimited(tag("\"\"\""), take_until("\"\"\""), tag("\"\"\"")),
|
delimited(tag("\"\"\""), take_until("\"\"\""), tag("\"\"\"")),
|
||||||
|val: Span| *val.fragment(),
|
|val: Span| *val.fragment(),
|
||||||
)
|
)(input)
|
||||||
.parse(input)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn string_content(input: Span) -> IResult<Span, &str> {
|
fn string_content(input: Span) -> IResult<Span, &str> {
|
||||||
|
@ -88,51 +84,49 @@ fn string_content(input: Span) -> IResult<Span, &str> {
|
||||||
}
|
}
|
||||||
'\n' if !escaped => {
|
'\n' if !escaped => {
|
||||||
let err = nom::error::Error {
|
let err = nom::error::Error {
|
||||||
input: input.take_from(j),
|
input: input.slice(j..),
|
||||||
code: nom::error::ErrorKind::Char,
|
code: nom::error::ErrorKind::Char,
|
||||||
};
|
};
|
||||||
return Err(nom::Err::Error(err));
|
return Err(nom::Err::Error(err));
|
||||||
}
|
}
|
||||||
'"' if !escaped => {
|
'"' if !escaped => {
|
||||||
return Ok((input.take_from(j), &buf[0..j]));
|
return Ok((input.slice(j..), &buf[0..j]));
|
||||||
}
|
}
|
||||||
_ => escaped = false,
|
_ => escaped = false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let err = nom::error::Error {
|
let err = nom::error::Error {
|
||||||
input: input.take_from(i + 1),
|
input: input.slice((i + 1)..),
|
||||||
code: nom::error::ErrorKind::Char,
|
code: nom::error::ErrorKind::Char,
|
||||||
};
|
};
|
||||||
Err(nom::Err::Failure(err))
|
Err(nom::Err::Failure(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delimited_string(input: Span) -> IResult<Span, &str> {
|
fn delimited_string(input: Span) -> IResult<Span, &str> {
|
||||||
preceded(char('"'), cut(terminated(string_content, char('"')))).parse(input)
|
preceded(char('"'), cut(terminated(string_content, char('"'))))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn string(input: Span) -> IResult<Span, &str> {
|
fn string(input: Span) -> IResult<Span, &str> {
|
||||||
alt((identifier, literal_string, delimited_string)).parse(input)
|
alt((identifier, literal_string, delimited_string))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn line_comment(input: Span) -> IResult<Span, &str> {
|
fn line_comment(input: Span) -> IResult<Span, &str> {
|
||||||
map(
|
map(
|
||||||
preceded(tag("//"), alt((not_line_ending, eof))),
|
preceded(tag("//"), alt((not_line_ending, eof))),
|
||||||
|val: Span| *val.fragment(),
|
|val: Span| *val.fragment(),
|
||||||
)
|
)(input)
|
||||||
.parse(input)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block_comment(input: Span) -> IResult<Span, &str> {
|
fn block_comment(input: Span) -> IResult<Span, &str> {
|
||||||
map(
|
map(
|
||||||
delimited(tag("/*"), take_until("*/"), tag("*/")),
|
delimited(tag("/*"), take_until("*/"), tag("*/")),
|
||||||
|val: Span| *val.fragment(),
|
|val: Span| *val.fragment(),
|
||||||
)
|
)(input)
|
||||||
.parse(input)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn comment(input: Span) -> IResult<Span, &str> {
|
fn comment(input: Span) -> IResult<Span, &str> {
|
||||||
alt((line_comment, block_comment)).parse(input)
|
alt((line_comment, block_comment))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn optional(input: Span) -> IResult<Span, ()> {
|
fn optional(input: Span) -> IResult<Span, ()> {
|
||||||
|
@ -141,7 +135,7 @@ fn optional(input: Span) -> IResult<Span, ()> {
|
||||||
let empty = value((), tag(""));
|
let empty = value((), tag(""));
|
||||||
let content = value((), many1_count(alt((whitespace, comment))));
|
let content = value((), many1_count(alt((whitespace, comment))));
|
||||||
|
|
||||||
alt((content, empty)).parse(input)
|
alt((content, empty))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn parse_next_token(input: Span) -> IResult<Span, Token> {
|
pub(crate) fn parse_next_token(input: Span) -> IResult<Span, Token> {
|
||||||
|
@ -165,48 +159,45 @@ pub(crate) fn parse_next_token(input: Span) -> IResult<Span, Token> {
|
||||||
map(float, Token::Float),
|
map(float, Token::Float),
|
||||||
map(string, |val| Token::String(val.to_string())),
|
map(string, |val| Token::String(val.to_string())),
|
||||||
)),
|
)),
|
||||||
)
|
)(input)
|
||||||
.parse(input)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn parse_trailing_characters(input: Span) -> IResult<Span, ()> {
|
pub(crate) fn parse_trailing_characters(input: Span) -> IResult<Span, ()> {
|
||||||
value((), optional).parse(input)
|
value((), optional)(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn parse_null(input: Span) -> IResult<Span, Token> {
|
pub(crate) fn parse_null(input: Span) -> IResult<Span, Token> {
|
||||||
preceded(optional, value(Token::Null, null)).parse(input)
|
preceded(optional, value(Token::Null, null))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn parse_separator(input: Span) -> IResult<Span, Token> {
|
pub(crate) fn parse_separator(input: Span) -> IResult<Span, Token> {
|
||||||
preceded(
|
preceded(
|
||||||
opt(horizontal_whitespace),
|
opt(horizontal_whitespace),
|
||||||
value(Token::Separator, separator),
|
value(Token::Separator, separator),
|
||||||
)
|
)(input)
|
||||||
.parse(input)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn parse_bool(input: Span) -> IResult<Span, Token> {
|
pub(crate) fn parse_bool(input: Span) -> IResult<Span, Token> {
|
||||||
preceded(optional, map(bool, Token::Boolean)).parse(input)
|
preceded(optional, map(bool, Token::Boolean))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn parse_integer(input: Span) -> IResult<Span, Token> {
|
pub(crate) fn parse_integer(input: Span) -> IResult<Span, Token> {
|
||||||
preceded(optional, map(integer, Token::Integer)).parse(input)
|
preceded(optional, map(integer, Token::Integer))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn parse_float(input: Span) -> IResult<Span, Token> {
|
pub(crate) fn parse_float(input: Span) -> IResult<Span, Token> {
|
||||||
preceded(optional, map(float, Token::Float)).parse(input)
|
preceded(optional, map(float, Token::Float))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn parse_identifier(input: Span) -> IResult<Span, Token> {
|
pub(crate) fn parse_identifier(input: Span) -> IResult<Span, Token> {
|
||||||
preceded(
|
preceded(
|
||||||
optional,
|
optional,
|
||||||
map(identifier, |val| Token::String(val.to_string())),
|
map(identifier, |val| Token::String(val.to_string())),
|
||||||
)
|
)(input)
|
||||||
.parse(input)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn parse_string(input: Span) -> IResult<Span, Token> {
|
pub(crate) fn parse_string(input: Span) -> IResult<Span, Token> {
|
||||||
preceded(optional, map(string, |val| Token::String(val.to_string()))).parse(input)
|
preceded(optional, map(string, |val| Token::String(val.to_string())))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
69
src/ser.rs
69
src/ser.rs
|
@ -7,14 +7,12 @@ use crate::error::{Error, ErrorCode, Result};
|
||||||
// TODO: Make configurable
|
// TODO: Make configurable
|
||||||
const INDENT: [u8; 2] = [0x20, 0x20];
|
const INDENT: [u8; 2] = [0x20, 0x20];
|
||||||
|
|
||||||
/// A container for serializing Rust values into SJSON.
|
|
||||||
pub struct Serializer<W> {
|
pub struct Serializer<W> {
|
||||||
// The current indentation level
|
// The current indentation level
|
||||||
level: usize,
|
level: usize,
|
||||||
writer: W,
|
writer: W,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serializes a value into a generic `io::Write`.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn to_writer<T, W>(writer: &mut W, value: &T) -> Result<()>
|
pub fn to_writer<T, W>(writer: &mut W, value: &T) -> Result<()>
|
||||||
where
|
where
|
||||||
|
@ -25,7 +23,6 @@ where
|
||||||
value.serialize(&mut serializer)
|
value.serialize(&mut serializer)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serializes a value into a byte vector.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn to_vec<T>(value: &T) -> Result<Vec<u8>>
|
pub fn to_vec<T>(value: &T) -> Result<Vec<u8>>
|
||||||
where
|
where
|
||||||
|
@ -36,7 +33,6 @@ where
|
||||||
Ok(vec)
|
Ok(vec)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serializes a value into a string.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn to_string<T>(value: &T) -> Result<String>
|
pub fn to_string<T>(value: &T) -> Result<String>
|
||||||
where
|
where
|
||||||
|
@ -55,7 +51,6 @@ impl<W> Serializer<W>
|
||||||
where
|
where
|
||||||
W: io::Write,
|
W: io::Write,
|
||||||
{
|
{
|
||||||
/// Creates a new `Serializer`.
|
|
||||||
pub fn new(writer: W) -> Self {
|
pub fn new(writer: W) -> Self {
|
||||||
Self { level: 0, writer }
|
Self { level: 0, writer }
|
||||||
}
|
}
|
||||||
|
@ -84,7 +79,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W> serde::ser::Serializer for &mut Serializer<W>
|
impl<'a, W> serde::ser::Serializer for &'a mut Serializer<W>
|
||||||
where
|
where
|
||||||
W: io::Write,
|
W: io::Write,
|
||||||
{
|
{
|
||||||
|
@ -216,9 +211,9 @@ where
|
||||||
self.serialize_unit()
|
self.serialize_unit()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_some<T>(self, value: &T) -> Result<Self::Ok>
|
fn serialize_some<T: ?Sized>(self, value: &T) -> Result<Self::Ok>
|
||||||
where
|
where
|
||||||
T: serde::Serialize + ?Sized,
|
T: serde::Serialize,
|
||||||
{
|
{
|
||||||
self.ensure_top_level_struct()?;
|
self.ensure_top_level_struct()?;
|
||||||
|
|
||||||
|
@ -245,9 +240,9 @@ where
|
||||||
self.serialize_str(variant)
|
self.serialize_str(variant)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Self::Ok>
|
fn serialize_newtype_struct<T: ?Sized>(self, _name: &'static str, value: &T) -> Result<Self::Ok>
|
||||||
where
|
where
|
||||||
T: serde::Serialize + ?Sized,
|
T: serde::Serialize,
|
||||||
{
|
{
|
||||||
self.ensure_top_level_struct()?;
|
self.ensure_top_level_struct()?;
|
||||||
|
|
||||||
|
@ -255,7 +250,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serialize an externally tagged enum: `{ NAME = VALUE }`.
|
// Serialize an externally tagged enum: `{ NAME = VALUE }`.
|
||||||
fn serialize_newtype_variant<T>(
|
fn serialize_newtype_variant<T: ?Sized>(
|
||||||
self,
|
self,
|
||||||
_name: &'static str,
|
_name: &'static str,
|
||||||
_variant_index: u32,
|
_variant_index: u32,
|
||||||
|
@ -263,7 +258,7 @@ where
|
||||||
value: &T,
|
value: &T,
|
||||||
) -> Result<Self::Ok>
|
) -> Result<Self::Ok>
|
||||||
where
|
where
|
||||||
T: serde::Serialize + ?Sized,
|
T: serde::Serialize,
|
||||||
{
|
{
|
||||||
self.ensure_top_level_struct()?;
|
self.ensure_top_level_struct()?;
|
||||||
|
|
||||||
|
@ -345,24 +340,24 @@ where
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_str<T>(self, value: &T) -> Result<Self::Ok>
|
fn collect_str<T: ?Sized>(self, value: &T) -> Result<Self::Ok>
|
||||||
where
|
where
|
||||||
T: std::fmt::Display + ?Sized,
|
T: std::fmt::Display,
|
||||||
{
|
{
|
||||||
self.serialize_str(&value.to_string())
|
self.serialize_str(&value.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W> serde::ser::SerializeSeq for &mut Serializer<W>
|
impl<'a, W> serde::ser::SerializeSeq for &'a mut Serializer<W>
|
||||||
where
|
where
|
||||||
W: io::Write,
|
W: io::Write,
|
||||||
{
|
{
|
||||||
type Ok = ();
|
type Ok = ();
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
|
fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<()>
|
||||||
where
|
where
|
||||||
T: Serialize + ?Sized,
|
T: Serialize,
|
||||||
{
|
{
|
||||||
self.add_indent()?;
|
self.add_indent()?;
|
||||||
value.serialize(&mut **self)?;
|
value.serialize(&mut **self)?;
|
||||||
|
@ -376,16 +371,16 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W> serde::ser::SerializeTuple for &mut Serializer<W>
|
impl<'a, W> serde::ser::SerializeTuple for &'a mut Serializer<W>
|
||||||
where
|
where
|
||||||
W: io::Write,
|
W: io::Write,
|
||||||
{
|
{
|
||||||
type Ok = ();
|
type Ok = ();
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
|
fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<()>
|
||||||
where
|
where
|
||||||
T: Serialize + ?Sized,
|
T: Serialize,
|
||||||
{
|
{
|
||||||
self.add_indent()?;
|
self.add_indent()?;
|
||||||
value.serialize(&mut **self)?;
|
value.serialize(&mut **self)?;
|
||||||
|
@ -399,16 +394,16 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W> serde::ser::SerializeTupleStruct for &mut Serializer<W>
|
impl<'a, W> serde::ser::SerializeTupleStruct for &'a mut Serializer<W>
|
||||||
where
|
where
|
||||||
W: io::Write,
|
W: io::Write,
|
||||||
{
|
{
|
||||||
type Ok = ();
|
type Ok = ();
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
|
fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<()>
|
||||||
where
|
where
|
||||||
T: Serialize + ?Sized,
|
T: Serialize,
|
||||||
{
|
{
|
||||||
self.add_indent()?;
|
self.add_indent()?;
|
||||||
value.serialize(&mut **self)?;
|
value.serialize(&mut **self)?;
|
||||||
|
@ -422,16 +417,16 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W> serde::ser::SerializeTupleVariant for &mut Serializer<W>
|
impl<'a, W> serde::ser::SerializeTupleVariant for &'a mut Serializer<W>
|
||||||
where
|
where
|
||||||
W: io::Write,
|
W: io::Write,
|
||||||
{
|
{
|
||||||
type Ok = ();
|
type Ok = ();
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
|
fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<()>
|
||||||
where
|
where
|
||||||
T: Serialize + ?Sized,
|
T: Serialize,
|
||||||
{
|
{
|
||||||
self.add_indent()?;
|
self.add_indent()?;
|
||||||
value.serialize(&mut **self)?;
|
value.serialize(&mut **self)?;
|
||||||
|
@ -454,24 +449,24 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W> serde::ser::SerializeMap for &mut Serializer<W>
|
impl<'a, W> serde::ser::SerializeMap for &'a mut Serializer<W>
|
||||||
where
|
where
|
||||||
W: io::Write,
|
W: io::Write,
|
||||||
{
|
{
|
||||||
type Ok = ();
|
type Ok = ();
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
fn serialize_key<T>(&mut self, key: &T) -> Result<()>
|
fn serialize_key<T: ?Sized>(&mut self, key: &T) -> Result<()>
|
||||||
where
|
where
|
||||||
T: Serialize + ?Sized,
|
T: Serialize,
|
||||||
{
|
{
|
||||||
self.add_indent()?;
|
self.add_indent()?;
|
||||||
key.serialize(&mut **self)
|
key.serialize(&mut **self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_value<T>(&mut self, value: &T) -> Result<()>
|
fn serialize_value<T: ?Sized>(&mut self, value: &T) -> Result<()>
|
||||||
where
|
where
|
||||||
T: Serialize + ?Sized,
|
T: Serialize,
|
||||||
{
|
{
|
||||||
// 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
|
||||||
|
@ -491,16 +486,16 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W> serde::ser::SerializeStruct for &mut Serializer<W>
|
impl<'a, W> serde::ser::SerializeStruct for &'a mut Serializer<W>
|
||||||
where
|
where
|
||||||
W: io::Write,
|
W: io::Write,
|
||||||
{
|
{
|
||||||
type Ok = ();
|
type Ok = ();
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
|
fn serialize_field<T: ?Sized>(&mut self, key: &'static str, value: &T) -> Result<()>
|
||||||
where
|
where
|
||||||
T: Serialize + ?Sized,
|
T: Serialize,
|
||||||
{
|
{
|
||||||
self.add_indent()?;
|
self.add_indent()?;
|
||||||
key.serialize(&mut **self)?;
|
key.serialize(&mut **self)?;
|
||||||
|
@ -521,16 +516,16 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W> serde::ser::SerializeStructVariant for &mut Serializer<W>
|
impl<'a, W> serde::ser::SerializeStructVariant for &'a mut Serializer<W>
|
||||||
where
|
where
|
||||||
W: std::io::Write,
|
W: std::io::Write,
|
||||||
{
|
{
|
||||||
type Ok = ();
|
type Ok = ();
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
|
fn serialize_field<T: ?Sized>(&mut self, key: &'static str, value: &T) -> Result<()>
|
||||||
where
|
where
|
||||||
T: Serialize + ?Sized,
|
T: Serialize,
|
||||||
{
|
{
|
||||||
self.add_indent()?;
|
self.add_indent()?;
|
||||||
key.serialize(&mut **self)?;
|
key.serialize(&mut **self)?;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use serde_sjson::to_string;
|
use serde_sjson::{to_string, Error};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn serialize_null() {
|
fn serialize_null() {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue