use std::num::{NonZeroU64, NonZeroUsize};
use haematite_query::Ready;
use haematite_query::page::Row;
use serde_json::value::RawValue;
use serde_json::{Value, json};
use super::super::Rows;
use super::super::origins::Named;
use super::super::source::Scope;
use crate::serve::storage::{Captured, Failure};
pub(super) fn rows(rows: Vec<Row>, form: Rows) -> Result<Box<RawValue>, Failure> {
match form {
Rows::Values => serde_json::value::to_raw_value(&rows),
Rows::Origins => {
let named: Vec<Named> = rows
.into_iter()
.map(|row| Named { origin: row.origin })
.collect();
serde_json::value::to_raw_value(&named)
}
}
.map_err(Failure::storage)
}
pub(super) fn fitted(
envelope: Value,
rows: &RawValue,
budget: NonZeroUsize,
) -> Result<Box<RawValue>, Failure> {
let answer = crate::serve::changes::with_rows(envelope, rows)?;
if answer.get().len() > budget.get() {
return Err(Failure::new(
"response_limit",
"the answer exceeds its requested bytes",
));
}
Ok(answer)
}
pub(super) fn rows_bytes(budget: NonZeroUsize, envelope: &Value) -> Result<NonZeroU64, Failure> {
let envelope = serde_json::to_vec(envelope)
.map_err(Failure::storage)?
.len();
let needed = envelope - 2;
u64::try_from(budget.get())
.ok()
.and_then(|budget| budget.checked_sub(u64::try_from(needed).ok()?))
.and_then(NonZeroU64::new)
.ok_or_else(|| {
Failure::new(
"response_limit",
format!(
"the answer's envelope takes {needed} bytes before any row; ask for more than that"
),
)
})
}
pub(super) fn engine(ready: &Ready) -> Value {
json!({
"engine": ready.engine,
"worker": ready.worker,
"protocol": ready.protocol,
"jaq_core": ready.jaq_core,
"jaq_std": ready.jaq_std,
"jaq_json": ready.jaq_json,
})
}
pub(super) fn named(scope: &Scope) -> Option<&str> {
match scope {
Scope::Collection(name) => Some(name),
Scope::All => None,
}
}
pub(super) struct Head<'a> {
pub(super) stages: &'a [&'static str],
pub(super) reading: &'a str,
pub(super) collection: Option<&'a str>,
pub(super) read: u64,
pub(super) skipped: u64,
pub(super) search: &'a Value,
pub(super) unscored: Option<u64>,
pub(super) next: Option<&'a str>,
}
pub(super) fn answer(at: &Captured, rows: &Value, engine: &Value, head: &Head<'_>) -> Value {
json!({
"selection": {
"branch": at.branch,
"version": at.version,
"roots": at.shards.iter().map(|shard| {
json!({"shard": shard.shard_id, "root": shard.head.to_string()})
}).collect::<Vec<_>>(),
},
"rows": rows,
"next": head.next,
"engine": engine,
"reading": head.reading,
"stages": head.stages,
"collection": head.collection,
"records_read": head.read,
"skipped": head.skipped,
"search": head.search,
"unscored": head.unscored,
})
}