Skip to content

Commit

Permalink
Expanded tests for non-alloc
Browse files Browse the repository at this point in the history
Two of the suites now work without alloc, and Parser has new functions
that just verify a JSON payload.
  • Loading branch information
ecton committed Jan 23, 2023
1 parent 2e29483 commit 330dbd6
Show file tree
Hide file tree
Showing 9 changed files with 1,128 additions and 89 deletions.
8 changes: 0 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,6 @@ debug = true
name = "roundtrip"
required-features = ["alloc"]

[[test]]
name = "jsonorg"
required-features = ["alloc"]

[[test]]
name = "jsontestsuite"
required-features = ["alloc"]

[[example]]
name = "borrowed-value"
required-features = ["alloc"]
Expand Down
22 changes: 12 additions & 10 deletions src/cow.rs → src/anystr.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
use core::ops::Deref;

/// A Cow-like type that only offers an `Owned` variant with `#[cfg(feature =
/// "alloc")]`.
/// A string that can be either Owned or Borrowed.
///
/// This type is similar to the standard library's Cow, but the `Owned` variant
/// is only available when the `alloc` or `std` features are enabled.
#[derive(Debug, Clone)]
pub enum CowStr<'a> {
pub enum AnyStr<'a> {
/// An owned String.
#[cfg(feature = "alloc")]
Owned(alloc::string::String),
/// A borrowed string slice.
Borrowed(&'a str),
}

impl<'a> AsRef<str> for CowStr<'a> {
impl<'a> AsRef<str> for AnyStr<'a> {
fn as_ref(&self) -> &str {
match self {
#[cfg(feature = "alloc")]
Expand All @@ -21,27 +23,27 @@ impl<'a> AsRef<str> for CowStr<'a> {
}
}

impl<'a> Eq for CowStr<'a> {}
impl<'a> Eq for AnyStr<'a> {}

impl<'a, 'b> PartialEq<CowStr<'b>> for CowStr<'a> {
fn eq(&self, other: &CowStr<'b>) -> bool {
impl<'a, 'b> PartialEq<AnyStr<'b>> for AnyStr<'a> {
fn eq(&self, other: &AnyStr<'b>) -> bool {
self.as_ref() == other.as_ref()
}
}

impl<'a, 'b> PartialEq<&'b str> for CowStr<'a> {
impl<'a, 'b> PartialEq<&'b str> for AnyStr<'a> {
fn eq(&self, other: &&'b str) -> bool {
self == *other
}
}

impl<'a> PartialEq<str> for CowStr<'a> {
impl<'a> PartialEq<str> for AnyStr<'a> {
fn eq(&self, other: &str) -> bool {
self.as_ref() == other
}
}

impl<'a> Deref for CowStr<'a> {
impl<'a> Deref for AnyStr<'a> {
type Target = str;

fn deref(&self) -> &Self::Target {
Expand Down
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ extern crate std;
#[cfg(feature = "alloc")]
extern crate alloc;

pub use crate::cow::CowStr;
pub use crate::anystr::AnyStr;
pub use crate::error::{Error, ErrorKind};
pub use crate::number::JsonNumber;
pub use crate::string::{JsonString, JsonStringInfo};
#[cfg(feature = "alloc")]
pub use crate::value::{Object, Value};

mod cow;
mod anystr;
/// A JSON DOM representation with minimal processing.
#[cfg(feature = "alloc")]
pub mod doc;
Expand Down
30 changes: 27 additions & 3 deletions src/number.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use crate::cow::CowStr;
use crate::anystr::AnyStr;
use crate::parser::{ParseDelegate, Parser};
use crate::{Error, ErrorKind};

/// A JSON-encoded number.
#[derive(Debug, Eq, PartialEq, Clone)]
pub struct JsonNumber<'a> {
/// The JSON source for this number.
pub(crate) source: CowStr<'a>,
pub(crate) source: AnyStr<'a>,
}

impl<'a> JsonNumber<'a> {
Expand Down Expand Up @@ -139,7 +139,7 @@ fn json_number_from_json() {
assert_eq!(
JsonNumber::from_json("1").unwrap(),
JsonNumber {
source: CowStr::Borrowed("1")
source: AnyStr::Borrowed("1")
}
);

Expand All @@ -157,3 +157,27 @@ fn json_number_conversions() {
assert_eq!(one.as_u64().unwrap(), 1);
assert!((one.as_f64().unwrap() - 1.0).abs() < f64::EPSILON);
}

#[test]
fn from_json_bad_types() {
assert_eq!(
JsonNumber::from_json("\"\"").unwrap_err().kind,
ErrorKind::ExpectedNumber
);
assert_eq!(
JsonNumber::from_json("null").unwrap_err().kind,
ErrorKind::ExpectedNumber
);
assert_eq!(
JsonNumber::from_json("true").unwrap_err().kind,
ErrorKind::ExpectedNumber
);
assert_eq!(
JsonNumber::from_json("[]").unwrap_err().kind,
ErrorKind::ExpectedNumber
);
assert_eq!(
JsonNumber::from_json("{}").unwrap_err().kind,
ErrorKind::ExpectedNumber
);
}
128 changes: 125 additions & 3 deletions src/parser.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use core::convert::Infallible;
use core::iter::Peekable;
use core::marker::PhantomData;
use core::slice;

use crate::cow::CowStr;
use crate::anystr::AnyStr;
use crate::string::{
merge_surrogate_pair, StringContents, HEX_OFFSET_TABLE, HIGH_SURROGATE_MAX, HIGH_SURROGATE_MIN,
LOW_SURROGATE_MAX, LOW_SURROGATE_MIN, SAFE_STRING_BYTES, SAFE_STRING_BYTES_VERIFY_UTF8,
Expand Down Expand Up @@ -50,6 +51,21 @@ impl<'a> Parser<'a, false> {
{
Self::parse_bytes(value, config, delegate)
}

/// Validates that `json` contains valid JSON and returns the kind of data
/// the payload contained.
pub fn validate_json_bytes(json: &'a [u8]) -> Result<JsonKind, Error> {
Self::validate_json_bytes_with_config(json, ParseConfig::default())
}

/// Validates that `json` contains valid JSON, using the settings from
/// `config`, and returns the kind of data the payload contained.
pub fn validate_json_bytes_with_config(
json: &'a [u8],
config: ParseConfig,
) -> Result<JsonKind, Error> {
Self::parse_bytes(json, config, ())
}
}

impl<'a> Parser<'a, true> {
Expand Down Expand Up @@ -80,6 +96,21 @@ impl<'a> Parser<'a, true> {
{
Self::parse_bytes(value.as_bytes(), config, delegate)
}

/// Validates that `json` contains valid JSON and returns the kind of data
/// the payload contained.
pub fn validate_json(json: &'a str) -> Result<JsonKind, Error> {
Self::validate_json_with_config(json, ParseConfig::default())
}

/// Validates that `json` contains valid JSON, using the settings from
/// `config`, and returns the kind of data the payload contained.
pub fn validate_json_with_config(
json: &'a str,
config: ParseConfig,
) -> Result<JsonKind, Error> {
Self::parse_json_with_config(json, config, ())
}
}

impl<'a, const GUARANTEED_UTF8: bool> Parser<'a, GUARANTEED_UTF8> {
Expand Down Expand Up @@ -420,7 +451,7 @@ impl<'a, const GUARANTEED_UTF8: bool> Parser<'a, GUARANTEED_UTF8> {
b'"' => {
break Ok(JsonString {
source: StringContents::Json {
source: CowStr::Borrowed(unsafe {
source: AnyStr::Borrowed(unsafe {
core::str::from_utf8_unchecked(
&self.source.bytes[start + 1..offset],
)
Expand Down Expand Up @@ -632,7 +663,7 @@ impl<'a, const GUARANTEED_UTF8: bool> Parser<'a, GUARANTEED_UTF8> {
}

Ok(JsonNumber {
source: CowStr::Borrowed(unsafe {
source: AnyStr::Borrowed(unsafe {
core::str::from_utf8_unchecked(&self.source.bytes[start..self.source.offset])
}),
})
Expand Down Expand Up @@ -968,3 +999,94 @@ pub enum JsonKind {
/// A list of values.
Array,
}

impl<'a> ParseDelegate<'a> for () {
type Array = usize;
type Error = Infallible;
type Key = ();
type Object = usize;
type Value = JsonKind;

fn null(&mut self) -> Result<Self::Value, Self::Error> {
Ok(JsonKind::Null)
}

fn boolean(&mut self, _value: bool) -> Result<Self::Value, Self::Error> {
Ok(JsonKind::Boolean)
}

fn number(&mut self, _value: JsonNumber<'a>) -> Result<Self::Value, Self::Error> {
Ok(JsonKind::Number)
}

fn string(&mut self, _value: JsonString<'a>) -> Result<Self::Value, Self::Error> {
Ok(JsonKind::String)
}

fn begin_object(&mut self) -> Result<Self::Object, Self::Error> {
Ok(0)
}

fn object_key(
&mut self,
_object: &mut Self::Object,
_key: JsonString<'a>,
) -> Result<Self::Key, Self::Error> {
Ok(())
}

fn object_value(
&mut self,
object: &mut Self::Object,
_key: Self::Key,
_value: Self::Value,
) -> Result<(), Self::Error> {
*object += 1;
Ok(())
}

fn object_is_empty(&self, object: &Self::Object) -> bool {
*object == 0
}

fn end_object(&mut self, _object: Self::Object) -> Result<Self::Value, Self::Error> {
Ok(JsonKind::Object)
}

fn begin_array(&mut self) -> Result<Self::Array, Self::Error> {
Ok(0)
}

fn array_value(
&mut self,
array: &mut Self::Array,
_value: Self::Value,
) -> Result<(), Self::Error> {
*array += 1;
Ok(())
}

fn array_is_empty(&self, array: &Self::Array) -> bool {
*array == 0
}

fn end_array(&mut self, _array: Self::Array) -> Result<Self::Value, Self::Error> {
Ok(JsonKind::Array)
}

fn kind_of(&self, value: &Self::Value) -> JsonKind {
*value
}
}

#[test]
fn validates() {
assert_eq!(
Parser::validate_json(r#"{"a":1,"b":true,"c":"hello","d":[],"e":{}}"#),
Ok(JsonKind::Object)
);
assert_eq!(
Parser::validate_json_bytes(br#"{"a":1,"b":true,"c":"hello","d":[],"e":{}}"#),
Ok(JsonKind::Object)
);
}
Loading

0 comments on commit 330dbd6

Please sign in to comment.