use std::sync::Arc;
use vantage_rhai::rhai::Dynamic;
use vantage_rhai::{Block, Env, Host, Limits};
use super::handle::Handle;
use super::vocab::{DataVocab, Terminals, Writes};
use crate::VistaResolver;
pub const MIN_LIMIT: usize = 1;
pub const MAX_LIMIT: usize = 50;
pub const DEFAULT_LIMIT: usize = 5;
fn run_once(host: &Host, script: &str) -> Result<Dynamic, String> {
host.compile_uncached(&Block::from(script))
.and_then(|s| s.eval(&Env::new()))
.map_err(|e| e.to_string())
}
pub async fn run_script(
script: String,
resolver: Arc<dyn VistaResolver>,
limit: usize,
writes: Writes,
) -> Result<serde_json::Value, String> {
let limit = limit.clamp(MIN_LIMIT, MAX_LIMIT);
tokio::task::spawn_blocking(move || {
let terminals = Terminals::ReadWrite {
limit: Some(limit),
writes,
};
let host = Host::builder(Limits::background())
.vocab(DataVocab::new(terminals).with_resolver(resolver))
.build();
run_once(&host, &script).map(|v| vantage_rhai::to_json(&v))
})
.await
.map_err(|e| format!("data script task failed to run: {e}"))?
}
pub fn preview_script(
script: String,
resolver: Arc<dyn VistaResolver>,
) -> Result<serde_json::Value, String> {
let host = Host::builder(Limits::background())
.vocab(DataVocab::new(Terminals::Describe).with_resolver(resolver.clone()))
.build();
let handle = run_once(&host, &script)?
.try_cast::<Handle>()
.ok_or_else(|| {
"preview script must end on the query itself, e.g. \
`table(\"orders\").where(\"status\", \"paid\")`; this host has no \
terminal verbs (`list`, `count`, `first`)"
.to_string()
})?;
let vista = handle
.resolve(Some(resolver.as_ref()))
.map_err(|e| e.to_string())?;
let mut out = serde_json::to_value(vista.preview()).map_err(|e| e.to_string())?;
if let Some(fields) = out.as_object_mut() {
fields.insert("driver".into(), vista.driver_name().into());
}
Ok(out)
}