Skip to content

Commit

Permalink
Merge pull request #267 from nox/display
Browse files Browse the repository at this point in the history
Redefine Serializer::collect_str
  • Loading branch information
dtolnay committed Mar 6, 2017
2 parents 419cd59 + 1d6a1c6 commit 97c184f
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 10 deletions.
2 changes: 1 addition & 1 deletion json/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ travis-ci = { repository = "serde-rs/json" }
preserve_order = ["linked-hash-map"]

[dependencies]
serde = "0.9.4"
serde = "0.9.11"
num-traits = "0.1.32"
linked-hash-map = { version = "0.4.1", optional = true }
itoa = "0.3"
Expand Down
77 changes: 69 additions & 8 deletions json/src/ser.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
//! Serialize a Rust data structure into JSON data.

use std::fmt;
use std::io;
use std::num::FpCategory;
use std::str;

use serde::ser::{self, Impossible};
use super::error::{Error, ErrorCode, Result};
Expand Down Expand Up @@ -307,6 +309,50 @@ impl<'a, W, F> ser::Serializer for &'a mut Serializer<W, F>
try!(self.formatter.begin_object_value(&mut self.writer));
self.serialize_map(Some(len))
}

fn collect_str<T: ?Sized>(self, value: &T) -> Result<Self::Ok>
where T: fmt::Display,
{
use std::fmt::Write;

struct Adapter<'ser, W: 'ser, F: 'ser> {
writer: &'ser mut W,
formatter: &'ser mut F,
error: Option<Error>,
}

impl<'ser, W, F> Write for Adapter<'ser, W, F>
where W: io::Write,
F: Formatter,
{
fn write_str(&mut self, s: &str) -> fmt::Result {
assert!(self.error.is_none());
match format_escaped_str_contents(self.writer, self.formatter, s) {
Ok(()) => Ok(()),
Err(err) => {
self.error = Some(err);
Err(fmt::Error)
}
}
}
}

try!(self.formatter.begin_string(&mut self.writer));
{
let mut adapter = Adapter {
writer: &mut self.writer,
formatter: &mut self.formatter,
error: None
};
match write!(adapter, "{}", value) {
Ok(()) => assert!(adapter.error.is_none()),
Err(_) => {
return Err(adapter.error.expect("there should be an error"));
},
}
}
self.formatter.end_string(&mut self.writer)
}
}

#[doc(hidden)]
Expand Down Expand Up @@ -1178,9 +1224,20 @@ fn format_escaped_str<W: ?Sized, F: ?Sized>(writer: &mut W, formatter: &mut F, v
where W: io::Write,
F: Formatter
{
let bytes = value.as_bytes();

try!(formatter.begin_string(writer));
try!(format_escaped_str_contents(writer, formatter, value));
try!(formatter.end_string(writer));
Ok(())
}

fn format_escaped_str_contents<W: ?Sized, F: ?Sized>(writer: &mut W,
formatter: &mut F,
value: &str)
-> Result<()>
where W: io::Write,
F: Formatter,
{
let bytes = value.as_bytes();

let mut start = 0;

Expand All @@ -1204,7 +1261,6 @@ fn format_escaped_str<W: ?Sized, F: ?Sized>(writer: &mut W, formatter: &mut F, v
try!(formatter.write_string_fragment(writer, &bytes[start..]));
}

try!(formatter.end_string(writer));
Ok(())
}

Expand Down Expand Up @@ -1245,11 +1301,16 @@ fn format_escaped_char<W: ?Sized, F: ?Sized>(wr: &mut W, formatter: &mut F, valu
where W: io::Write,
F: Formatter,
{
// FIXME: this allocation is required in order to be compatible with stable
// rust, which doesn't support encoding a `char` into a stack buffer.
let mut s = String::new();
s.push(value);
format_escaped_str(wr, formatter, &s)
use std::io::Write;
// A char encoded as UTF-8 takes 4 bytes at most.
let mut buf = [0; 4];
write!(&mut buf[..], "{}", value).unwrap();
// Writing a char successfully always produce valid UTF-8.
// Once we do not support Rust <1.15 we will be able to just use
// the method `char::encode_utf8`.
// See https://github.com/serde-rs/json/issues/270.
let slice = unsafe { str::from_utf8_unchecked(&buf[0..value.len_utf8()]) };
format_escaped_str(wr, formatter, slice)
}

/// Serialize the given data structure as JSON into the IO stream.
Expand Down
2 changes: 1 addition & 1 deletion json_tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ publish = false
trace-macros = []

[dependencies]
serde = "0.9"
serde = "0.9.11"
serde_json = { path = "../json" }
serde_derive = "0.9"

Expand Down
42 changes: 42 additions & 0 deletions json_tests/tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,25 @@ fn test_write_bool() {
test_pretty_encode_ok(tests);
}

#[test]
fn test_write_char() {
let tests = &[
('n', "\"n\""),
('"', "\"\\\"\""),
('\\', "\"\\\\\""),
('/', "\"/\""),
('\x08', "\"\\b\""),
('\x0C', "\"\\f\""),
('\n', "\"\\n\""),
('\r', "\"\\r\""),
('\t', "\"\\t\""),
('\x0B', "\"\\u000b\""),
('\u{3A3}', "\"\u{3A3}\""),
];
test_encode_ok(tests);
test_pretty_encode_ok(tests);
}

#[test]
fn test_write_list() {
test_encode_ok(&[
Expand Down Expand Up @@ -801,6 +820,29 @@ fn test_parse_bool() {
]);
}

#[test]
fn test_parse_char() {
test_parse_err::<char>(vec![
("\"ab\"", "invalid value: string \"ab\", expected a character at line 1 column 4"),
("10", "invalid type: integer `10`, expected a character at line 1 column 2"),
]);

test_parse_ok(vec![
("\"n\"", 'n'),
("\"\\\"\"", '"'),
("\"\\\\\"", '\\'),
("\"/\"", '/'),
("\"\\b\"", '\x08'),
("\"\\f\"", '\x0C'),
("\"\\n\"", '\n'),
("\"\\r\"", '\r'),
("\"\\t\"", '\t'),
("\"\\u000b\"", '\x0B'),
("\"\\u000B\"", '\x0B'),
("\"\u{3A3}\"", '\u{3A3}'),
]);
}

#[test]
fn test_parse_number_errors() {
test_parse_err::<f64>(vec![
Expand Down

0 comments on commit 97c184f

Please sign in to comment.