///|
using @hashmap {type HashMap}
///|
fn parse_error_has_label(error : @parsec.ParseError, label : String) -> Bool {
match error {
Expected(label=actual, ..) => actual == label
ExpectedAny(labels~, ..) => labels.contains(label)
NotFollowedBy(label=actual, ..) => actual == label
Context(cause~, ..) => parse_error_has_label(cause, label)
_ => false
}
}
///|
fn error_from_parse(
error : @parsec.ParseError,
limits : JsonLimits,
) -> JsonError {
let offset = error.offset()
if parse_error_has_label(error, depth_limit_label) {
Limit(kind=NestingDepth, offset~, limit=limits.max_depth)
} else if parse_error_has_label(error, string_limit_label) {
Limit(kind=StringChars, offset~, limit=limits.max_string_chars)
} else if parse_error_has_label(error, array_limit_label) {
Limit(kind=ArrayItems, offset~, limit=limits.max_array_items)
} else if parse_error_has_label(error, object_limit_label) {
Limit(kind=ObjectMembers, offset~, limit=limits.max_object_members)
} else {
Syntax(error~)
}
}
///|
fn validate_items(items : Array[RawJson]) -> Result[Array[Json], JsonError] {
let values = []
let mut failure : JsonError? = None
for item in items {
match failure {
Some(_) => ()
None =>
match validate(item) {
Ok(value) => values.push(value)
Err(error) => failure = Some(error)
}
}
}
match failure {
Some(error) => Err(error)
None => Ok(values)
}
}
///|
fn validate_members(
members : Array[RawMember],
seen : @hashmap.HashMap[String, Int],
) -> Result[Array[(String, Json)], JsonError] {
let values = []
let mut failure : JsonError? = None
for property in members {
match failure {
Some(_) => ()
None =>
match seen.get(property.key) {
Some(first_offset) =>
failure = Some(
DuplicateKey(
key=property.key,
first_offset~,
duplicate_offset=property.key_offset,
),
)
None =>
match validate(property.value) {
Ok(value) => {
seen.set(property.key, property.key_offset)
values.push((property.key, value))
}
Err(error) => failure = Some(error)
}
}
}
}
match failure {
Some(error) => Err(error)
None => Ok(values)
}
}
///|
fn validate(raw : RawJson) -> Result[Json, JsonError] {
match raw {
Null => Ok(Null)
Bool(value) => Ok(Bool(value~))
Number(raw) => Ok(Number(raw~))
Text(value) => Ok(Text(value~))
Array(items) =>
match validate_items(items) {
Ok(values) => Ok(Array(items=values))
Err(error) => Err(error)
}
Object(members) => {
let seen : HashMap[String, Int] = HashMap([], capacity=members.length())
match validate_members(members, seen) {
Ok(values) => Ok(Object(members=values))
Err(error) => Err(error)
}
}
}
}
///|
pub fn parse_with_limits(
input : String,
limits : JsonLimits,
) -> Result[Json, JsonError] {
let chars = input.to_array()
if chars.length() > limits.max_input_chars {
Err(
Limit(
kind=InputChars,
offset=limits.max_input_chars,
limit=limits.max_input_chars,
),
)
} else {
let parser = json_whitespace()
.then_right(value(limits, 0))
.then_left(@parsec.Parser::eof())
match parser.parse(chars) {
Ok(raw) => validate(raw)
Err(error) => Err(error_from_parse(error, limits))
}
}
}
///|
pub fn parse(input : String) -> Result[Json, JsonError] {
parse_with_limits(input, JsonLimits::standard())
}