use core::{fmt, str};
use serde::de::{self, Visitor};
use self::enum_::UnitVariantAccess;
use self::map::MapAccess;
use self::seq::SeqAccess;
mod enum_;
mod map;
mod seq;
pub type Result<T> = core::result::Result<T, Error>;
#[derive(Debug, PartialEq)]
pub enum Error {
EofWhileParsingList,
EofWhileParsingObject,
EofWhileParsingString,
EofWhileParsingValue,
ExpectedColon,
ExpectedListCommaOrEnd,
ExpectedObjectCommaOrEnd,
ExpectedSomeIdent,
ExpectedSomeValue,
InvalidNumber,
InvalidType,
InvalidUnicodeCodePoint,
KeyMustBeAString,
TrailingCharacters,
TrailingComma,
#[doc(hidden)]
__Extensible,
}
#[cfg(feature = "std")]
impl ::std::error::Error for Error {
fn description(&self) -> &str {
""
}
}
pub(crate) struct Deserializer<'b> {
slice: &'b [u8],
index: usize,
}
impl<'a> Deserializer<'a> {
fn new(slice: &'a [u8]) -> Deserializer<'_> {
Deserializer { slice, index: 0 }
}
fn eat_char(&mut self) {
self.index += 1;
}
fn end(&mut self) -> Result<()> {
match self.parse_whitespace() {
Some(_) => Err(Error::TrailingCharacters),
None => Ok(()),
}
}
fn end_seq(&mut self) -> Result<()> {
match self.parse_whitespace().ok_or(Error::EofWhileParsingList)? {
b']' => {
self.eat_char();
Ok(())
}
b',' => {
self.eat_char();
match self.parse_whitespace() {
Some(b']') => Err(Error::TrailingComma),
_ => Err(Error::TrailingCharacters),
}
}
_ => Err(Error::TrailingCharacters),
}
}
fn end_map(&mut self) -> Result<()> {
match self
.parse_whitespace()
.ok_or(Error::EofWhileParsingObject)?
{
b'}' => {
self.eat_char();
Ok(())
}
b',' => Err(Error::TrailingComma),
_ => Err(Error::TrailingCharacters),
}
}
fn next_char(&mut self) -> Option<u8> {
let ch = self.slice.get(self.index);
if ch.is_some() {
self.index += 1;
}
ch.cloned()
}
fn parse_ident(&mut self, ident: &[u8]) -> Result<()> {
for c in ident {
if Some(*c) != self.next_char() {
return Err(Error::ExpectedSomeIdent);
}
}
Ok(())
}
fn parse_object_colon(&mut self) -> Result<()> {
match self
.parse_whitespace()
.ok_or(Error::EofWhileParsingObject)?
{
b':' => {
self.eat_char();
Ok(())
}
_ => Err(Error::ExpectedColon),
}
}
fn parse_str(&mut self) -> Result<&'a str> {
let start = self.index;
loop {
match self.peek() {
Some(b'"') => {
let end = self.index;
self.eat_char();
return str::from_utf8(&self.slice[start..end])
.map_err(|_| Error::InvalidUnicodeCodePoint);
}
Some(_) => self.eat_char(),
None => return Err(Error::EofWhileParsingString),
}
}
}
fn parse_whitespace(&mut self) -> Option<u8> {
loop {
match self.peek() {
Some(b' ') | Some(b'\n') | Some(b'\t') | Some(b'\r') => {
self.eat_char();
}
other => {
return other;
}
}
}
}
fn peek(&mut self) -> Option<u8> {
self.slice.get(self.index).cloned()
}
}
macro_rules! deserialize_unsigned {
($self:ident, $visitor:ident, $uxx:ident, $visit_uxx:ident) => {{
let peek = $self
.parse_whitespace()
.ok_or(Error::EofWhileParsingValue)?;
match peek {
b'-' => Err(Error::InvalidNumber),
b'0' => {
$self.eat_char();
$visitor.$visit_uxx(0)
}
b'1'..=b'9' => {
$self.eat_char();
let mut number = (peek - b'0') as $uxx;
loop {
match $self.peek() {
Some(c @ b'0'..=b'9') => {
$self.eat_char();
number = number
.checked_mul(10)
.ok_or(Error::InvalidNumber)?
.checked_add((c - b'0') as $uxx)
.ok_or(Error::InvalidNumber)?;
}
_ => return $visitor.$visit_uxx(number),
}
}
}
_ => Err(Error::InvalidType),
}
}};
}
macro_rules! deserialize_signed {
($self:ident, $visitor:ident, $ixx:ident, $visit_ixx:ident) => {{
let signed = match $self
.parse_whitespace()
.ok_or(Error::EofWhileParsingValue)?
{
b'-' => {
$self.eat_char();
true
}
_ => false,
};
match $self.peek().ok_or(Error::EofWhileParsingValue)? {
b'0' => {
$self.eat_char();
$visitor.$visit_ixx(0)
}
c @ b'1'..=b'9' => {
$self.eat_char();
let mut number = (c - b'0') as $ixx * if signed { -1 } else { 1 };
loop {
match $self.peek() {
Some(c @ b'0'..=b'9') => {
$self.eat_char();
number = number
.checked_mul(10)
.ok_or(Error::InvalidNumber)?
.checked_add((c - b'0') as $ixx * if signed { -1 } else { 1 })
.ok_or(Error::InvalidNumber)?;
}
_ => return $visitor.$visit_ixx(number),
}
}
}
_ => return Err(Error::InvalidType),
}
}};
}
impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> {
type Error = Error;
fn deserialize_any<V>(self, _visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
unreachable!()
}
fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let peek = self.parse_whitespace().ok_or(Error::EofWhileParsingValue)?;
match peek {
b't' => {
self.eat_char();
self.parse_ident(b"rue")?;
visitor.visit_bool(true)
}
b'f' => {
self.eat_char();
self.parse_ident(b"alse")?;
visitor.visit_bool(false)
}
_ => Err(Error::InvalidType),
}
}
fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
deserialize_signed!(self, visitor, i8, visit_i8)
}
fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
deserialize_signed!(self, visitor, i16, visit_i16)
}
fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
deserialize_signed!(self, visitor, i32, visit_i32)
}
fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
deserialize_signed!(self, visitor, i64, visit_i64)
}
fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
deserialize_unsigned!(self, visitor, u8, visit_u8)
}
fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
deserialize_unsigned!(self, visitor, u16, visit_u16)
}
fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
deserialize_unsigned!(self, visitor, u32, visit_u32)
}
fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
deserialize_unsigned!(self, visitor, u64, visit_u64)
}
fn deserialize_f32<V>(self, _visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
unreachable!()
}
fn deserialize_f64<V>(self, _visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
unreachable!()
}
fn deserialize_char<V>(self, _visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
unreachable!()
}
fn deserialize_str<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let peek = self.parse_whitespace().ok_or(Error::EofWhileParsingValue)?;
match peek {
b'"' => {
self.eat_char();
visitor.visit_borrowed_str(self.parse_str()?)
}
_ => Err(Error::InvalidType),
}
}
fn deserialize_string<V>(self, _visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
unreachable!()
}
fn deserialize_bytes<V>(self, _visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
unreachable!()
}
fn deserialize_byte_buf<V>(self, _visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
unreachable!()
}
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
match self.parse_whitespace().ok_or(Error::EofWhileParsingValue)? {
b'n' => {
self.eat_char();
self.parse_ident(b"ull")?;
visitor.visit_none()
}
_ => visitor.visit_some(self),
}
}
fn deserialize_unit<V>(self, _visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
unreachable!()
}
fn deserialize_unit_struct<V>(self, _name: &'static str, _visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
unreachable!()
}
fn deserialize_newtype_struct<V>(self, _name: &'static str, _visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
unreachable!()
}
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
match self.peek().ok_or(Error::EofWhileParsingValue)? {
b'[' => {
self.eat_char();
let ret = visitor.visit_seq(SeqAccess::new(self))?;
self.end_seq()?;
Ok(ret)
}
_ => Err(Error::InvalidType),
}
}
fn deserialize_tuple<V>(self, _len: usize, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
self.deserialize_seq(visitor)
}
fn deserialize_tuple_struct<V>(
self,
_name: &'static str,
_len: usize,
_visitor: V,
) -> Result<V::Value>
where
V: Visitor<'de>,
{
unreachable!()
}
fn deserialize_map<V>(self, _visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
unreachable!()
}
fn deserialize_struct<V>(
self,
_name: &'static str,
_fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value>
where
V: Visitor<'de>,
{
let peek = self.parse_whitespace().ok_or(Error::EofWhileParsingValue)?;
if peek == b'{' {
self.eat_char();
let ret = visitor.visit_map(MapAccess::new(self))?;
self.end_map()?;
Ok(ret)
} else {
Err(Error::InvalidType)
}
}
fn deserialize_enum<V>(
self,
_name: &'static str,
_variants: &'static [&'static str],
visitor: V,
) -> Result<V::Value>
where
V: Visitor<'de>,
{
match self.parse_whitespace().ok_or(Error::EofWhileParsingValue)? {
b'"' => visitor.visit_enum(UnitVariantAccess::new(self)),
_ => Err(Error::ExpectedSomeValue),
}
}
fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
self.deserialize_str(visitor)
}
fn deserialize_ignored_any<V>(self, _visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
unreachable!()
}
}
impl de::Error for Error {
fn custom<T>(_msg: T) -> Self
where
T: fmt::Display,
{
unreachable!()
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
Error::EofWhileParsingList => "EOF while parsing a list.",
Error::EofWhileParsingObject => "EOF while parsing an object.",
Error::EofWhileParsingString => "EOF while parsing a string.",
Error::EofWhileParsingValue => "EOF while parsing a JSON value.",
Error::ExpectedColon => "Expected this character to be a `':'`.",
Error::ExpectedListCommaOrEnd => {
"Expected this character to be either a `','` or\
a \
`']'`."
}
Error::ExpectedObjectCommaOrEnd => {
"Expected this character to be either a `','` \
or a \
`'}'`."
}
Error::ExpectedSomeIdent => {
"Expected to parse either a `true`, `false`, or a \
`null`."
}
Error::ExpectedSomeValue => "Expected this character to start a JSON value.",
Error::InvalidNumber => "Invalid number.",
Error::InvalidType => "Invalid type",
Error::InvalidUnicodeCodePoint => "Invalid unicode code point.",
Error::KeyMustBeAString => "Object key is not a string.",
Error::TrailingCharacters => {
"JSON has non-whitespace trailing characters after \
the \
value."
}
Error::TrailingComma => "JSON has a comma after the last value in an array or map.",
_ => "Invalid JSON",
}
)
}
}
pub fn from_slice<'a, T>(v: &'a [u8]) -> Result<T>
where
T: de::Deserialize<'a>,
{
let mut de = Deserializer::new(v);
let value = de::Deserialize::deserialize(&mut de)?;
de.end()?;
Ok(value)
}
pub fn from_str<'a, T>(s: &'a str) -> Result<T>
where
T: de::Deserialize<'a>,
{
from_slice(s.as_bytes())
}
#[cfg(test)]
mod tests {
use serde_derive::Deserialize;
#[derive(Debug, Deserialize, PartialEq)]
enum Type {
#[serde(rename = "boolean")]
Boolean,
#[serde(rename = "number")]
Number,
#[serde(rename = "thing")]
Thing,
}
#[test]
fn array() {
assert_eq!(crate::from_str::<[i32; 0]>("[]"), Ok([]));
assert_eq!(crate::from_str("[0, 1, 2]"), Ok([0, 1, 2]));
assert!(crate::from_str::<[i32; 2]>("[0, 1,]").is_err());
}
#[test]
fn bool() {
assert_eq!(crate::from_str("true"), Ok(true));
assert_eq!(crate::from_str(" true"), Ok(true));
assert_eq!(crate::from_str("true "), Ok(true));
assert_eq!(crate::from_str("false"), Ok(false));
assert_eq!(crate::from_str(" false"), Ok(false));
assert_eq!(crate::from_str("false "), Ok(false));
assert!(crate::from_str::<bool>("true false").is_err());
assert!(crate::from_str::<bool>("tru").is_err());
}
#[test]
fn enum_clike() {
assert_eq!(crate::from_str(r#" "boolean" "#), Ok(Type::Boolean));
assert_eq!(crate::from_str(r#" "number" "#), Ok(Type::Number));
assert_eq!(crate::from_str(r#" "thing" "#), Ok(Type::Thing));
}
#[test]
fn str() {
assert_eq!(crate::from_str(r#" "hello" "#), Ok("hello"));
}
#[test]
fn struct_bool() {
#[derive(Debug, Deserialize, PartialEq)]
struct Led {
led: bool,
}
assert_eq!(crate::from_str(r#"{ "led": true }"#), Ok(Led { led: true }));
assert_eq!(
crate::from_str(r#"{ "led": false }"#),
Ok(Led { led: false })
);
}
#[test]
fn struct_i8() {
#[derive(Debug, Deserialize, PartialEq)]
struct Temperature {
temperature: i8,
}
assert_eq!(
crate::from_str(r#"{ "temperature": -17 }"#),
Ok(Temperature { temperature: -17 })
);
assert_eq!(
crate::from_str(r#"{ "temperature": -0 }"#),
Ok(Temperature { temperature: -0 })
);
assert_eq!(
crate::from_str(r#"{ "temperature": 0 }"#),
Ok(Temperature { temperature: 0 })
);
assert!(crate::from_str::<Temperature>(r#"{ "temperature": 128 }"#).is_err());
assert!(crate::from_str::<Temperature>(r#"{ "temperature": -129 }"#).is_err());
}
#[test]
fn struct_option() {
#[derive(Debug, Deserialize, PartialEq)]
struct Property<'a> {
#[serde(borrow)]
description: Option<&'a str>,
}
assert_eq!(
crate::from_str(r#"{ "description": "An ambient temperature sensor" }"#),
Ok(Property {
description: Some("An ambient temperature sensor"),
})
);
assert_eq!(
crate::from_str(r#"{ "description": null }"#),
Ok(Property { description: None })
);
assert_eq!(crate::from_str(r#"{}"#), Ok(Property { description: None }));
}
#[test]
fn struct_u8() {
#[derive(Debug, Deserialize, PartialEq)]
struct Temperature {
temperature: u8,
}
assert_eq!(
crate::from_str(r#"{ "temperature": 20 }"#),
Ok(Temperature { temperature: 20 })
);
assert_eq!(
crate::from_str(r#"{ "temperature": 0 }"#),
Ok(Temperature { temperature: 0 })
);
assert!(crate::from_str::<Temperature>(r#"{ "temperature": 256 }"#).is_err());
assert!(crate::from_str::<Temperature>(r#"{ "temperature": -1 }"#).is_err());
}
#[test]
#[ignore]
fn wot() {
#[derive(Debug, Deserialize, PartialEq)]
struct Thing<'a> {
#[serde(borrow)]
properties: Properties<'a>,
#[serde(rename = "type")]
ty: Type,
}
#[derive(Debug, Deserialize, PartialEq)]
struct Properties<'a> {
#[serde(borrow)]
temperature: Property<'a>,
#[serde(borrow)]
humidity: Property<'a>,
#[serde(borrow)]
led: Property<'a>,
}
#[derive(Debug, Deserialize, PartialEq)]
struct Property<'a> {
#[serde(rename = "type")]
ty: Type,
unit: Option<&'a str>,
#[serde(borrow)]
description: Option<&'a str>,
href: &'a str,
}
assert_eq!(
crate::from_str::<Thing<'_>>(
r#"
{
"type": "thing",
"properties": {
"temperature": {
"type": "number",
"unit": "celsius",
"description": "An ambient temperature sensor",
"href": "/properties/temperature"
},
"humidity": {
"type": "number",
"unit": "percent",
"href": "/properties/humidity"
},
"led": {
"type": "boolean",
"description": "A red LED",
"href": "/properties/led"
}
}
}
"#
),
Ok(Thing {
properties: Properties {
temperature: Property {
ty: Type::Number,
unit: Some("celcius"),
description: Some("An ambient temperature sensor"),
href: "/properties/temperature",
},
humidity: Property {
ty: Type::Number,
unit: Some("percent"),
description: None,
href: "/properties/humidity",
},
led: Property {
ty: Type::Boolean,
unit: None,
description: Some("A red LED"),
href: "/properties/led",
},
},
ty: Type::Thing,
})
)
}
}