use ciborium::Value as CborValue;
use rhai::{Array, Dynamic, Engine, EvalAltResult, Map as RhaiMap};
use vantage_dataset::ReadableValueSet;
use vantage_types::Record;
use super::conventional::RhaiVista;
use super::convert::record_to_dynamic;
use super::introspect::{capabilities_map, columns_array, references_array};
use crate::vista::Vista;
type RhaiResult<T> = Result<T, Box<EvalAltResult>>;
pub fn register_fetch_verbs(engine: &mut Engine, limit: usize) {
engine.register_fn("list", move |v: &mut RhaiVista| -> RhaiResult<Array> {
read_vista(v, |vista| {
let rows = block_on(fetch_capped(vista, limit)).map_err(vantage_err)?;
Ok(rows
.iter()
.map(|(_id, rec)| record_to_dynamic(rec))
.collect())
})
});
engine.register_fn("get_some", |v: &mut RhaiVista| -> RhaiResult<Dynamic> {
read_vista(v, |vista| {
match block_on(vista.get_some_value()).map_err(vantage_err)? {
Some((_id, rec)) => Ok(record_to_dynamic(&rec)),
None => Ok(Dynamic::UNIT),
}
})
});
engine.register_fn("count", |v: &mut RhaiVista| -> RhaiResult<i64> {
read_vista(v, |vista| {
if !vista.capabilities().can_count {
return Err("count: this backend does not support counting".into());
}
block_on(vista.get_count()).map_err(vantage_err)
})
});
engine.register_fn("capabilities", |v: &mut RhaiVista| -> RhaiResult<RhaiMap> {
read_vista(v, |vista| Ok(capabilities_map(vista)))
});
engine.register_fn("columns", |v: &mut RhaiVista| -> RhaiResult<Array> {
read_vista(v, |vista| Ok(columns_array(vista)))
});
engine.register_fn("references", |v: &mut RhaiVista| -> RhaiResult<Array> {
read_vista(v, |vista| Ok(references_array(vista)))
});
}
async fn fetch_capped(
vista: &Vista,
limit: usize,
) -> vantage_core::Result<Vec<(String, Record<CborValue>)>> {
if vista.capabilities().can_fetch_window {
match vista.fetch_window(0, limit).await {
Ok(rows) => return Ok(rows),
Err(e) if e.is_unsupported() => {}
Err(e) => return Err(e),
}
}
let all = vista.list_values().await?;
Ok(all.into_iter().take(limit).collect())
}
fn block_on<F: std::future::Future>(fut: F) -> F::Output {
tokio::runtime::Handle::current().block_on(fut)
}
fn read_vista<T>(v: &RhaiVista, f: impl FnOnce(&Vista) -> RhaiResult<T>) -> RhaiResult<T> {
let guard =
v.0.lock()
.map_err(|_| Box::<EvalAltResult>::from("RhaiVista mutex poisoned"))?;
let vista = guard
.as_ref()
.ok_or_else(|| Box::<EvalAltResult>::from("vista already consumed in script"))?;
f(vista)
}
fn vantage_err(e: vantage_core::VantageError) -> Box<EvalAltResult> {
Box::<EvalAltResult>::from(e.to_string())
}