commit
181bff2ee4
|
@ -55,7 +55,7 @@ struct Time {
|
||||||
hour: u8,
|
hour: u8,
|
||||||
minute: u8,
|
minute: u8,
|
||||||
second: u8,
|
second: u8,
|
||||||
secfract: f64,
|
nanosecond: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Clone)]
|
#[derive(PartialEq, Clone)]
|
||||||
|
@ -97,9 +97,9 @@ impl fmt::Display for Date {
|
||||||
impl fmt::Display for Time {
|
impl fmt::Display for Time {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "{:02}:{:02}:{:02}", self.hour, self.minute, self.second)?;
|
write!(f, "{:02}:{:02}:{:02}", self.hour, self.minute, self.second)?;
|
||||||
if self.secfract != 0.0 {
|
if self.nanosecond != 0 {
|
||||||
let s = format!("{}", self.secfract);
|
let s = format!("{:09}", self.nanosecond);
|
||||||
write!(f, "{}", s.trim_left_matches("0"))?;
|
write!(f, ".{}", s.trim_right_matches('0'))?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -199,38 +199,37 @@ impl FromStr for Datetime {
|
||||||
let s1 = digit(&mut chars)?;
|
let s1 = digit(&mut chars)?;
|
||||||
let s2 = digit(&mut chars)?;
|
let s2 = digit(&mut chars)?;
|
||||||
|
|
||||||
let secfract = if chars.clone().next() == Some('.') {
|
let mut nanosecond = 0;
|
||||||
|
if chars.clone().next() == Some('.') {
|
||||||
chars.next();
|
chars.next();
|
||||||
let mut first = true;
|
|
||||||
let whole = chars.as_str();
|
let whole = chars.as_str();
|
||||||
|
|
||||||
let mut end = whole.len();
|
let mut end = whole.len();
|
||||||
for (i, c) in whole.char_indices() {
|
for (i, byte) in whole.bytes().enumerate() {
|
||||||
match c {
|
match byte {
|
||||||
'0' ... '9' => {}
|
b'0' ... b'9' => {
|
||||||
|
if i < 9 {
|
||||||
|
let p = 10_u32.pow(8 - i as u32);
|
||||||
|
nanosecond += p * (byte - b'0') as u32;
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
end = i;
|
end = i;
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
first = false;
|
|
||||||
}
|
}
|
||||||
if first {
|
if end == 0 {
|
||||||
return Err(DatetimeParseError { _private: () })
|
return Err(DatetimeParseError { _private: () })
|
||||||
}
|
}
|
||||||
chars = whole[end..].chars();
|
chars = whole[end..].chars();
|
||||||
match format!("0.{}", &whole[..end]).parse() {
|
|
||||||
Ok(f) => f,
|
|
||||||
Err(_) => return Err(DatetimeParseError { _private: () }),
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
};
|
|
||||||
|
|
||||||
let time = Time {
|
let time = Time {
|
||||||
hour: h1 * 10 + h2,
|
hour: h1 * 10 + h2,
|
||||||
minute: m1 * 10 + m2,
|
minute: m1 * 10 + m2,
|
||||||
second: s1 * 10 + s2,
|
second: s1 * 10 + s2,
|
||||||
secfract: secfract,
|
nanosecond: nanosecond,
|
||||||
};
|
};
|
||||||
|
|
||||||
if time.hour > 24 {
|
if time.hour > 24 {
|
||||||
|
@ -239,7 +238,10 @@ impl FromStr for Datetime {
|
||||||
if time.minute > 59 {
|
if time.minute > 59 {
|
||||||
return Err(DatetimeParseError { _private: () })
|
return Err(DatetimeParseError { _private: () })
|
||||||
}
|
}
|
||||||
if time.second > 60 {
|
if time.second > 59 {
|
||||||
|
return Err(DatetimeParseError { _private: () })
|
||||||
|
}
|
||||||
|
if time.nanosecond > 999_999_999 {
|
||||||
return Err(DatetimeParseError { _private: () })
|
return Err(DatetimeParseError { _private: () })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
10
src/ser.rs
10
src/ser.rs
|
@ -107,7 +107,8 @@ pub enum Error {
|
||||||
/// attempted where the key of a map was not a string.
|
/// attempted where the key of a map was not a string.
|
||||||
KeyNotString,
|
KeyNotString,
|
||||||
|
|
||||||
/// Keys in maps are not allowed to have newlines.
|
/// An error that we never omit but keep for backwards compatibility
|
||||||
|
#[doc(hidden)]
|
||||||
KeyNewline,
|
KeyNewline,
|
||||||
|
|
||||||
/// Arrays in TOML must have a homogenous type, but a heterogeneous array
|
/// Arrays in TOML must have a homogenous type, but a heterogeneous array
|
||||||
|
@ -624,9 +625,6 @@ impl<'a, 'b> ser::SerializeMap for SerializeTable<'a, 'b> {
|
||||||
SerializeTable::Table { ref mut key, .. } => {
|
SerializeTable::Table { ref mut key, .. } => {
|
||||||
key.truncate(0);
|
key.truncate(0);
|
||||||
*key = input.serialize(StringExtractor)?;
|
*key = input.serialize(StringExtractor)?;
|
||||||
if key.contains('\n') {
|
|
||||||
return Err(Error::KeyNewline)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -1047,13 +1045,13 @@ impl fmt::Display for Error {
|
||||||
match *self {
|
match *self {
|
||||||
Error::UnsupportedType => "unsupported Rust type".fmt(f),
|
Error::UnsupportedType => "unsupported Rust type".fmt(f),
|
||||||
Error::KeyNotString => "map key was not a string".fmt(f),
|
Error::KeyNotString => "map key was not a string".fmt(f),
|
||||||
Error::KeyNewline => "map keys cannot contain newlines".fmt(f),
|
|
||||||
Error::ArrayMixedType => "arrays cannot have mixed types".fmt(f),
|
Error::ArrayMixedType => "arrays cannot have mixed types".fmt(f),
|
||||||
Error::ValueAfterTable => "values must be emitted before tables".fmt(f),
|
Error::ValueAfterTable => "values must be emitted before tables".fmt(f),
|
||||||
Error::DateInvalid => "a serialized date was invalid".fmt(f),
|
Error::DateInvalid => "a serialized date was invalid".fmt(f),
|
||||||
Error::NumberInvalid => "a serialized number was invalid".fmt(f),
|
Error::NumberInvalid => "a serialized number was invalid".fmt(f),
|
||||||
Error::UnsupportedNone => "unsupported None value".fmt(f),
|
Error::UnsupportedNone => "unsupported None value".fmt(f),
|
||||||
Error::Custom(ref s) => s.fmt(f),
|
Error::Custom(ref s) => s.fmt(f),
|
||||||
|
Error::KeyNewline => unreachable!(),
|
||||||
Error::__Nonexhaustive => panic!(),
|
Error::__Nonexhaustive => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1064,13 +1062,13 @@ impl error::Error for Error {
|
||||||
match *self {
|
match *self {
|
||||||
Error::UnsupportedType => "unsupported Rust type",
|
Error::UnsupportedType => "unsupported Rust type",
|
||||||
Error::KeyNotString => "map key was not a string",
|
Error::KeyNotString => "map key was not a string",
|
||||||
Error::KeyNewline => "map keys cannot contain newlines",
|
|
||||||
Error::ArrayMixedType => "arrays cannot have mixed types",
|
Error::ArrayMixedType => "arrays cannot have mixed types",
|
||||||
Error::ValueAfterTable => "values must be emitted before tables",
|
Error::ValueAfterTable => "values must be emitted before tables",
|
||||||
Error::DateInvalid => "a serialized date was invalid",
|
Error::DateInvalid => "a serialized date was invalid",
|
||||||
Error::NumberInvalid => "a serialized number was invalid",
|
Error::NumberInvalid => "a serialized number was invalid",
|
||||||
Error::UnsupportedNone => "unsupported None value",
|
Error::UnsupportedNone => "unsupported None value",
|
||||||
Error::Custom(_) => "custom error",
|
Error::Custom(_) => "custom error",
|
||||||
|
Error::KeyNewline => unreachable!(),
|
||||||
Error::__Nonexhaustive => panic!(),
|
Error::__Nonexhaustive => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -463,7 +463,7 @@ fn datetimes() {
|
||||||
t!("2016-09-09T09:09:09Z");
|
t!("2016-09-09T09:09:09Z");
|
||||||
t!("2016-09-09T09:09:09.1Z");
|
t!("2016-09-09T09:09:09.1Z");
|
||||||
t!("2016-09-09T09:09:09.2+10:00");
|
t!("2016-09-09T09:09:09.2+10:00");
|
||||||
t!("2016-09-09T09:09:09.0123456789-02:00");
|
t!("2016-09-09T09:09:09.123456789-02:00");
|
||||||
bad!("foo = 2016-09-09T09:09:09.Z", "failed to parse date");
|
bad!("foo = 2016-09-09T09:09:09.Z", "failed to parse date");
|
||||||
bad!("foo = 2016-9-09T09:09:09Z", "failed to parse date");
|
bad!("foo = 2016-9-09T09:09:09Z", "failed to parse date");
|
||||||
bad!("foo = 2016-09-09T09:09:09+2:00", "failed to parse date");
|
bad!("foo = 2016-09-09T09:09:09+2:00", "failed to parse date");
|
||||||
|
|
|
@ -190,6 +190,12 @@ test!(example_bom,
|
||||||
include_str!("valid/example-bom.toml"),
|
include_str!("valid/example-bom.toml"),
|
||||||
include_str!("valid/example.json"));
|
include_str!("valid/example.json"));
|
||||||
|
|
||||||
|
test!(datetime_truncate,
|
||||||
|
include_str!("valid/datetime-truncate.toml"),
|
||||||
|
include_str!("valid/datetime-truncate.json"));
|
||||||
|
test!(key_quote_newline,
|
||||||
|
include_str!("valid/key-quote-newline.toml"),
|
||||||
|
include_str!("valid/key-quote-newline.json"));
|
||||||
test!(table_array_nest_no_keys,
|
test!(table_array_nest_no_keys,
|
||||||
include_str!("valid/table-array-nest-no-keys.toml"),
|
include_str!("valid/table-array-nest-no-keys.toml"),
|
||||||
include_str!("valid/table-array-nest-no-keys.json"));
|
include_str!("valid/table-array-nest-no-keys.json"));
|
||||||
|
|
6
tests/valid/datetime-truncate.json
Normal file
6
tests/valid/datetime-truncate.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"bestdayever": {
|
||||||
|
"type": "datetime",
|
||||||
|
"value": "1987-07-05T17:45:00.123456789Z"
|
||||||
|
}
|
||||||
|
}
|
1
tests/valid/datetime-truncate.toml
Normal file
1
tests/valid/datetime-truncate.toml
Normal file
|
@ -0,0 +1 @@
|
||||||
|
bestdayever = 1987-07-05T17:45:00.123456789012345Z
|
3
tests/valid/key-quote-newline.json
Normal file
3
tests/valid/key-quote-newline.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"\n": {"type": "integer", "value": "1"}
|
||||||
|
}
|
1
tests/valid/key-quote-newline.toml
Normal file
1
tests/valid/key-quote-newline.toml
Normal file
|
@ -0,0 +1 @@
|
||||||
|
"\n" = 1
|
Loading…
Reference in a new issue