Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
300 changes: 281 additions & 19 deletions src/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ use super::prelude::*;

use core::convert::From;
use rjson::{Array, Null, Object, Value};
#[cfg(test)]
use std::collections::BTreeMap;

pub enum JsonValue {
Null,
Expand All @@ -12,7 +14,7 @@ pub enum JsonValue {
Object(BTreeMap<String, JsonValue>),
}

#[derive(Debug)]
#[derive(Ord, PartialOrd, Eq, PartialEq)]
pub enum JsonError {
NotJsonType,
MissingValue,
Expand All @@ -23,9 +25,17 @@ pub enum JsonError {
InvalidString,
InvalidArray,
ExpectedStringGotNumber,
OutOfRange(JsonOutOfRangeError),
}

#[derive(Debug)]
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)]
pub enum JsonOutOfRangeError {
OutOfRangeU8,
OutOfRangeU64,
OutOfRangeU128,
Comment thread
mrLSD marked this conversation as resolved.
}

#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)]
pub enum ParseError {
InvalidAccountId,
}
Expand All @@ -49,7 +59,15 @@ impl JsonValue {
pub fn u64(&self, key: &str) -> Result<u64, JsonError> {
match self {
JsonValue::Object(o) => match o.get(key).ok_or(JsonError::MissingValue)? {
JsonValue::Number(n) => Ok(*n as u64),
JsonValue::Number(n) => {
if n.is_sign_negative() || n.is_infinite() || n > &(u64::MAX as f64) {
Err(JsonError::OutOfRange(JsonOutOfRangeError::OutOfRangeU64))
} else if n.is_nan() {
Err(JsonError::InvalidU64)
} else {
Ok(*n as u64)
}
}
_ => Err(JsonError::InvalidU64),
},
_ => Err(JsonError::NotJsonType),
Expand Down Expand Up @@ -78,24 +96,16 @@ impl JsonValue {
#[allow(dead_code)]
pub fn parse_u8(v: &JsonValue) -> Result<u8, JsonError> {
match v {
JsonValue::Number(n) => Ok(*n as u8),
JsonValue::Number(n) => {
if n.is_sign_negative() || n > &(u8::MAX as f64) {
Err(JsonError::OutOfRange(JsonOutOfRangeError::OutOfRangeU8))
} else {
Ok(*n as u8)
}
}
_ => Err(JsonError::InvalidU8),
}
}

#[allow(dead_code)]
pub fn array<T, F>(&self, key: &str, call: F) -> Result<Vec<T>, JsonError>
where
F: FnMut(&JsonValue) -> T,
{
match self {
JsonValue::Object(o) => match o.get(key).ok_or(JsonError::MissingValue)? {
JsonValue::Array(arr) => Ok(arr.iter().map(call).collect()),
_ => Err(JsonError::InvalidArray),
},
_ => Err(JsonError::NotJsonType),
}
}
}

impl AsRef<[u8]> for JsonError {
Expand All @@ -110,10 +120,38 @@ impl AsRef<[u8]> for JsonError {
Self::InvalidString => b"ERR_FAILED_PARSE_STRING",
Self::InvalidArray => b"ERR_FAILED_PARSE_ARRAY",
Self::ExpectedStringGotNumber => b"ERR_EXPECTED_STRING_GOT_NUMBER",
Self::OutOfRange(err) => err.as_ref(),
}
}
}

impl AsRef<[u8]> for JsonOutOfRangeError {
fn as_ref(&self) -> &[u8] {
match self {
Self::OutOfRangeU8 => b"ERR_OUT_OF_RANGE_U8",
Self::OutOfRangeU64 => b"ERR_OUT_OF_RANGE_U64",
Self::OutOfRangeU128 => b"ERR_OUT_OF_RANGE_U128",
}
}
}

#[cfg(test)]
impl std::fmt::Debug for JsonError {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.write_fmt(format_args!(
"{}",
std::str::from_utf8(self.as_ref()).unwrap()
))
}
}

#[cfg(test)]
impl std::fmt::Display for JsonError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.write_fmt(format_args!("{:?}", *self))
}
}

impl Array<JsonValue, JsonObject, JsonValue> for JsonArray {
fn new() -> Self {
JsonArray(Vec::new())
Expand Down Expand Up @@ -175,7 +213,15 @@ impl TryFrom<&JsonValue> for u128 {

fn try_from(value: &JsonValue) -> Result<Self, Self::Error> {
match value {
JsonValue::String(n) => Ok(n.parse::<u128>().map_err(|_| JsonError::InvalidU128)?),
JsonValue::String(n) => {
if let Ok(x) = n.parse::<u128>() {
Ok(x)
} else if n.parse::<i128>().is_ok() {
Err(JsonError::OutOfRange(JsonOutOfRangeError::OutOfRangeU128))
} else {
Err(JsonError::InvalidU128)
}
}
JsonValue::Number(_) => Err(JsonError::ExpectedStringGotNumber),
_ => Err(JsonError::InvalidU128),
}
Expand Down Expand Up @@ -207,3 +253,219 @@ pub fn parse_json(data: &[u8]) -> Option<JsonValue> {
let mut index = 0;
rjson::parse::<JsonValue, JsonArray, JsonObject, JsonValue>(&*data_array, &mut index)
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_json_type_string() {
let json = parse_json(r#"{"foo": "abcd"}"#.as_bytes()).unwrap();
let string_data = json.string("foo").ok().unwrap();
assert_eq!(string_data, "abcd");

let json = parse_json(r#"{"foo": 123}"#.as_bytes()).unwrap();
let err = json.string("foo").unwrap_err();
assert_eq!(err, JsonError::InvalidString);

let json = parse_json(r#"{"foo": true}"#.as_bytes()).unwrap();
let err = json.string("foo").unwrap_err();
assert_eq!(err, JsonError::InvalidString);

let json = parse_json(r#"{"foo": ["abcd"]}"#.as_bytes()).unwrap();
let err = json.string("foo").unwrap_err();
assert_eq!(err, JsonError::InvalidString);

let json = parse_json(r#"{"foo": {}}"#.as_bytes()).unwrap();
let err = json.string("foo").unwrap_err();
assert_eq!(err, JsonError::InvalidString);

let json = parse_json(r#"{"foo": null}"#.as_bytes()).unwrap();
let err = json.string("foo").unwrap_err();
assert_eq!(err, JsonError::InvalidString);

let json = JsonValue::Null;
let err = json.string("foo").unwrap_err();
assert_eq!(err, JsonError::NotJsonType);
}

#[test]
fn test_json_type_u64() {
let json = parse_json(r#"{"foo": 123}"#.as_bytes()).unwrap();
let val = json.u64("foo").ok().unwrap();
assert_eq!(val, 123);

let json = parse_json(format!(r#"{{"foo": {} }}"#, u64::MAX).as_bytes()).unwrap();
let val = json.u64("foo").ok().unwrap();
assert_eq!(val, u64::MAX);

let json = parse_json(r#"{"foo": 12.99}"#.as_bytes()).unwrap();
// TODO [#176]: should fail since it is not a `u64`
let val = json.u64("foo").ok().unwrap();
assert_eq!(val, 12);

let json = parse_json(format!(r#"{{"foo": {} }}"#, u128::MAX).as_bytes()).unwrap();
let err = json.u64("foo").unwrap_err();
assert_eq!(
err,
JsonError::OutOfRange(JsonOutOfRangeError::OutOfRangeU64)
);

let json = parse_json(r#"{"foo": -123}"#.as_bytes()).unwrap();
Comment thread
mrLSD marked this conversation as resolved.
let err = json.u64("foo").unwrap_err();
assert_eq!(
err,
JsonError::OutOfRange(JsonOutOfRangeError::OutOfRangeU64)
);

let json = parse_json(r#"{"foo": "abcd"}"#.as_bytes()).unwrap();
let err = json.u64("foo").unwrap_err();
assert_eq!(err, JsonError::InvalidU64);

let json = parse_json(r#"{"foo": "123"}"#.as_bytes()).unwrap();
let err = json.u64("foo").unwrap_err();
assert_eq!(err, JsonError::InvalidU64);

let json = parse_json(r#"{"foo": true}"#.as_bytes()).unwrap();
let err = json.u64("foo").unwrap_err();
assert_eq!(err, JsonError::InvalidU64);

let json = parse_json(r#"{"foo": [123]}"#.as_bytes()).unwrap();
let err = json.u64("foo").unwrap_err();
assert_eq!(err, JsonError::InvalidU64);

let json = parse_json(r#"{"foo": {}}"#.as_bytes()).unwrap();
let err = json.u64("foo").unwrap_err();
assert_eq!(err, JsonError::InvalidU64);

let json = parse_json(r#"{"foo": null}"#.as_bytes()).unwrap();
let err = json.u64("foo").unwrap_err();
assert_eq!(err, JsonError::InvalidU64);

let json = JsonValue::Null;
let err = json.u64("foo").unwrap_err();
assert_eq!(err, JsonError::NotJsonType);
}

#[test]
fn test_json_type_u128() {
let json = parse_json(r#"{"foo": "123"}"#.as_bytes()).unwrap();
Comment thread
mrLSD marked this conversation as resolved.
let val = json.u128("foo").ok().unwrap();
assert_eq!(val, 123);

let json = parse_json(r#"{"foo": "-123"}"#.as_bytes()).unwrap();
let err = json.u128("foo").unwrap_err();
assert_eq!(
err,
JsonError::OutOfRange(JsonOutOfRangeError::OutOfRangeU128)
);

let json = parse_json(r#"{"foo": 123}"#.as_bytes()).unwrap();
let err = json.u128("foo").unwrap_err();
assert_eq!(err, JsonError::ExpectedStringGotNumber);

let json = parse_json(r#"{"foo": 12.3}"#.as_bytes()).unwrap();
let err = json.u128("foo").unwrap_err();
assert_eq!(err, JsonError::ExpectedStringGotNumber);

let json = parse_json(r#"{"foo": "12.3"}"#.as_bytes()).unwrap();
let err = json.u128("foo").unwrap_err();
assert_eq!(err, JsonError::InvalidU128);

let json = parse_json(r#"{"foo": "abcd"}"#.as_bytes()).unwrap();
let err = json.u128("foo").unwrap_err();
assert_eq!(err, JsonError::InvalidU128);

let json = parse_json(r#"{"foo": true}"#.as_bytes()).unwrap();
let err = json.u128("foo").unwrap_err();
assert_eq!(err, JsonError::InvalidU128);

let json = parse_json(r#"{"foo": ["123"]}"#.as_bytes()).unwrap();
let err = json.u128("foo").unwrap_err();
assert_eq!(err, JsonError::InvalidU128);

let json = parse_json(r#"{"foo": {}}"#.as_bytes()).unwrap();
let err = json.u128("foo").unwrap_err();
assert_eq!(err, JsonError::InvalidU128);

let json = parse_json(r#"{"foo": null}"#.as_bytes()).unwrap();
let err = json.u128("foo").unwrap_err();
assert_eq!(err, JsonError::InvalidU128);

let json = JsonValue::Null;
let err = json.u128("foo").unwrap_err();
assert_eq!(err, JsonError::NotJsonType);
}

#[test]
fn test_json_type_bool() {
let json = parse_json(r#"{"foo": true}"#.as_bytes()).unwrap();
let val = json.bool("foo").ok().unwrap();
assert_eq!(val, true);

let json = parse_json(r#"{"foo": false}"#.as_bytes()).unwrap();
let val = json.bool("foo").ok().unwrap();
assert_eq!(val, false);

let json = parse_json(r#"{"foo": "true"}"#.as_bytes()).unwrap();
let err = json.bool("foo").unwrap_err();
assert_eq!(err, JsonError::InvalidBool);

let json = parse_json(r#"{"foo": "false"}"#.as_bytes()).unwrap();
let err = json.bool("foo").unwrap_err();
assert_eq!(err, JsonError::InvalidBool);

let json = parse_json(r#"{"foo": [true]}"#.as_bytes()).unwrap();
let err = json.bool("foo").unwrap_err();
assert_eq!(err, JsonError::InvalidBool);

let json = parse_json(r#"{"foo": 123}"#.as_bytes()).unwrap();
let err = json.bool("foo").unwrap_err();
assert_eq!(err, JsonError::InvalidBool);

let json = parse_json(r#"{"foo": 12.3}"#.as_bytes()).unwrap();
let err = json.bool("foo").unwrap_err();
assert_eq!(err, JsonError::InvalidBool);

let json = parse_json(r#"{"foo": "abcd"}"#.as_bytes()).unwrap();
let err = json.bool("foo").unwrap_err();
assert_eq!(err, JsonError::InvalidBool);

let json = parse_json(r#"{"foo": {}}"#.as_bytes()).unwrap();
let err = json.bool("foo").unwrap_err();
assert_eq!(err, JsonError::InvalidBool);

let json = parse_json(r#"{"foo": null}"#.as_bytes()).unwrap();
let err = json.bool("foo").unwrap_err();
assert_eq!(err, JsonError::InvalidBool);

let json = JsonValue::Null;
let err = json.bool("foo").unwrap_err();
assert_eq!(err, JsonError::NotJsonType);
}

#[test]
fn test_json_type_u8() {
let json = JsonValue::from(123f64);
let val = JsonValue::parse_u8(&json).ok().unwrap();
assert_eq!(val, 123);

let json = JsonValue::from(-1f64);
let err = JsonValue::parse_u8(&json).unwrap_err();
assert_eq!(
err,
JsonError::OutOfRange(JsonOutOfRangeError::OutOfRangeU8)
);

let json = JsonValue::from(256f64);
let err = JsonValue::parse_u8(&json).unwrap_err();
assert_eq!(
err,
JsonError::OutOfRange(JsonOutOfRangeError::OutOfRangeU8)
);

let json = JsonValue::from("abcd".to_string());
let err = JsonValue::parse_u8(&json).unwrap_err();
assert_eq!(err, JsonError::InvalidU8);
}
}
3 changes: 1 addition & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ mod contract {
};

use crate::json::parse_json;
use crate::prelude::{format, Address, ToString, TryInto, H160, H256, U256};
use crate::prelude::{Address, ToString, TryInto, H160, H256, U256};
use crate::sdk;
use crate::storage::{bytes_to_key, KeyPrefix};
use crate::types::{
Expand Down Expand Up @@ -323,7 +323,6 @@ mod contract {
let args: NEP141FtOnTransferArgs = parse_json(sdk::read_input().as_slice())
.sdk_unwrap()
.try_into()
.map_err(|err| format!("ERR_JSON_{:?}", err))
.sdk_unwrap();

if sdk::predecessor_account_id() == sdk::current_account_id() {
Expand Down