///|
pub fn compile(
source : String,
limits? : Limits = default_limits(),
) -> Compiled raise JmesPathError {
validate_limits(limits)
{ source, expr: parse(source, limits), limits, }
}
///|
pub fn Compiled::search(
self : Compiled,
data : Json,
) -> Json raise JmesPathError {
check_json_size(data, false)
let state = { limits: self.limits, steps: 0, results: 0, depth: 0, }
let result = evaluate(self.expr, data, state)
check_json_size(result, false)
result
}
///|
pub fn search(
expression : String,
data : Json,
limits? : Limits = default_limits(),
) -> Json raise JmesPathError {
compile(expression, limits~).search(data)
}
///|
pub fn search_json(
expression : String,
json_text : String,
limits? : Limits = default_limits(),
) -> Json raise JmesPathError {
let data = parse_json_bounded(json_text)
search(expression, data, limits~)
}
///|
fn batch_error(diagnostic : Diagnostic) -> Json {
let message = if diagnostic.message.length() > 128 {
diagnostic.message[:128].to_owned() + "..."
} else {
diagnostic.message
}
Json::object(
Map([
("ok", Json::boolean(false)),
(
"error",
Json::object(
Map([
("code", Json::string(diagnostic.code)),
("offset", Json::number(diagnostic.offset.to_double())),
("message", Json::string(message)),
]),
),
),
]),
)
}
///|
/// Runs independent queries from a JSON array of `{expression, data}` objects.
/// Errors are returned per item, so one invalid query or malformed request does
/// not abort the batch. The top-level input must still be an array.
pub fn search_batch(
requests : Json,
limits? : Limits = default_limits(),
) -> Json raise JmesPathError {
validate_limits(limits)
guard requests is Array(items) else {
fail("invalid_batch", 0, "batch input must be a JSON array")
}
if items.length() > 10000 {
fail("limit_exceeded", 0, "batch exceeds 10000 requests")
}
let responses : Array[Json] = []
let mut response_nodes = 0
let mut response_units = 0
for item in items {
let response = match item {
Object(request) =>
match (request.get("expression"), request.get("data")) {
(Some(String(expression)), Some(data)) =>
try {
let response = Json::object(
Map([
("ok", Json::boolean(true)),
("result", search(expression, data, limits~)),
]),
)
// Reserve space for up to 10,000 small error responses. Successful
// replies share a budget; a large item must not prevent later small
// requests from running. Account for the outer array's indentation.
let size = measure_json(response, true, initial_depth=1)
let units = size.units
if size.nodes > 500000 - response_nodes ||
units > 8388608 - response_units {
fail("limit_exceeded", 0, "batch response budget exhausted")
}
response_nodes += size.nodes
response_units += units
response
} catch {
JmesPathError(diagnostic) => batch_error(diagnostic)
}
_ =>
batch_error({
code: "invalid_batch",
offset: 0,
message: "each request needs string expression and data",
})
}
_ =>
batch_error({
code: "invalid_batch",
offset: 0,
message: "each request needs string expression and data",
})
}
responses.push(response)
}
Json::array(responses)
}