diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fd9fac7..cd02584 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,8 +30,8 @@ jobs: # thumbv6m-none-eabi supports atomic, but not atomic CAS. # riscv32i-unknown-none-elf does not support atomic at all. target: - - thumbv6m-none-eabi - thumbv7m-none-eabi + - thumbv6m-none-eabi - riscv32i-unknown-none-elf runs-on: ubuntu-latest steps: @@ -45,6 +45,9 @@ jobs: working-directory: valuable - run: cargo hack build --target ${{ matrix.target }} --feature-powerset --skip std,default working-directory: valuable-serde + # TODO: support no-std in valuable-json + # - run: cargo hack build --target ${{ matrix.target }} --feature-powerset --skip std,default + # working-directory: valuable-json features: runs-on: ubuntu-latest diff --git a/Cargo.toml b/Cargo.toml index 92cffaf..a72d83c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ resolver = "2" members = [ "valuable", "valuable-derive", + "valuable-json", "valuable-serde", "tests", ] diff --git a/valuable-json/Cargo.toml b/valuable-json/Cargo.toml new file mode 100644 index 0000000..abd7e9c --- /dev/null +++ b/valuable-json/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "valuable-json" +version = "0.1.0" +authors = ["Taiki Endo "] +edition = "2018" +license = "MIT" +description = "JSON support for Valuable." + +[features] +default = ["std"] + +# NOTE: Disabling `std` feature is not supported yet. +std = ["valuable/std"] + +[dependencies] +valuable = { version = "0.1", path = "../valuable", default-features = false } +ryu = "1" + +[dev-dependencies] +valuable = { version = "0.1", path = "../valuable", features = ["derive"] } diff --git a/valuable-json/src/lib.rs b/valuable-json/src/lib.rs new file mode 100644 index 0000000..8827c26 --- /dev/null +++ b/valuable-json/src/lib.rs @@ -0,0 +1,766 @@ +#![warn( + missing_debug_implementations, + missing_docs, + rust_2018_idioms, + unreachable_pub +)] +#![cfg(feature = "std")] + +//! JSON support for Valuable. +//! +//! # Examples +//! +//! ``` +//! use valuable::Valuable; +//! +//! #[derive(Valuable)] +//! struct Point { +//! x: i32, +//! y: i32, +//! } +//! +//! let point = Point { x: 1, y: 2 }; +//! +//! assert_eq!( +//! valuable_json::to_string(&point).unwrap(), +//! r#"{"x":1,"y":2}"#, +//! ); +//! ``` + +use std::io; + +use valuable::*; + +macro_rules! try_block { + ($expr:expr) => { + (|| -> io::Result<_> { + $expr; + Ok(()) + })() + }; +} + +// TODO: should we define our own error type? +#[cold] +fn invalid_data(msg: &str) -> io::Error { + io::Error::new(io::ErrorKind::InvalidData, msg) +} + +/// Serialize the given value JSON into the IO stream. +pub fn to_writer(writer: W, value: &V) -> io::Result<()> +where + W: io::Write, + V: ?Sized + Valuable, +{ + Serializer::new(writer).serialize(value) +} + +/// Serialize the given value as pretty-printed JSON into the IO stream. +pub fn to_writer_pretty(writer: W, value: &V) -> io::Result<()> +where + W: io::Write, + V: ?Sized + Valuable, +{ + Serializer::pretty(writer).serialize(value) +} + +/// Serialize the given value as a byte vector of JSON. +pub fn to_vec(value: &V) -> io::Result> +where + V: ?Sized + Valuable, +{ + let mut out = Vec::with_capacity(128); + to_writer(&mut out, value)?; + Ok(out) +} + +/// Serialize the given value as a pretty-printed byte vector of JSON. +pub fn to_vec_pretty(value: &V) -> io::Result> +where + V: ?Sized + Valuable, +{ + let mut out = Vec::with_capacity(128); + to_writer_pretty(&mut out, value)?; + Ok(out) +} + +/// Serialize the given value as a string of JSON. +pub fn to_string(value: &V) -> io::Result +where + V: ?Sized + Valuable, +{ + let vec = to_vec(value)?; + if cfg!(debug_assertions) { + Ok(String::from_utf8(vec).unwrap()) + } else { + // SAFETY: We do not emit invalid UTF-8. + Ok(unsafe { String::from_utf8_unchecked(vec) }) + } +} + +/// Serialize the given value as a pretty-printed string of JSON. +pub fn to_string_pretty(value: &V) -> io::Result +where + V: ?Sized + Valuable, +{ + let vec = to_vec_pretty(value)?; + if cfg!(debug_assertions) { + Ok(String::from_utf8(vec).unwrap()) + } else { + // SAFETY: We do not emit invalid UTF-8. + Ok(unsafe { String::from_utf8_unchecked(vec) }) + } +} + +/// A JSON serializer. +#[derive(Debug)] +pub struct Serializer { + out: W, + option: SerializerOption, + error: Option, +} + +// TODO: Investigate what the better way to set these options is. +#[derive(Debug)] +struct SerializerOption { + reject_nan: bool, + escape_solidus: bool, + style: Option