use std::sync::Arc;
use futures::stream;
use surrealdb_types::ToSql;
use crate::exec::context::{ContextLevel, ExecutionContext};
use crate::exec::{
AccessMode, CardinalityHint, ExecOperator, FlowResult, OperatorMetrics, ValueBatch,
ValueBatchStream,
};
use crate::expr::Base;
use crate::iam::{Action, ResourceKind};
use crate::val::{Duration, Value};
#[derive(Debug, Clone)]
pub struct SleepPlan {
pub duration: Duration,
pub(crate) metrics: Arc<OperatorMetrics>,
}
impl SleepPlan {
pub(crate) fn new(duration: Duration) -> Self {
Self {
duration,
metrics: Arc::new(OperatorMetrics::new()),
}
}
}
impl ExecOperator for SleepPlan {
fn name(&self) -> &'static str {
"Sleep"
}
fn attrs(&self) -> Vec<(String, String)> {
vec![("duration".to_string(), self.duration.to_sql())]
}
fn required_context(&self) -> ContextLevel {
ContextLevel::Root
}
fn access_mode(&self) -> AccessMode {
AccessMode::ReadWrite
}
fn cardinality_hint(&self) -> CardinalityHint {
CardinalityHint::AtMostOne
}
fn metrics(&self) -> Option<&OperatorMetrics> {
Some(&self.metrics)
}
fn execute(&self, ctx: &ExecutionContext) -> FlowResult<ValueBatchStream> {
let duration = self.duration;
let ctx = ctx.clone();
Ok(Box::pin(stream::once(async move {
ctx.is_allowed(Action::Edit, ResourceKind::Table, Base::Root)?;
let effective_duration = match ctx.ctx().timeout() {
Some(remaining) if remaining < duration.0 => remaining,
_ => duration.0,
};
#[cfg(target_family = "wasm")]
wasmtimer::tokio::sleep(effective_duration).await;
#[cfg(not(target_family = "wasm"))]
tokio::select! {
_ = tokio::time::sleep(effective_duration) => {},
_ = ctx.cancellation().cancelled() => {},
}
Ok(ValueBatch {
values: vec![Value::None],
})
})))
}
fn is_scalar(&self) -> bool {
true
}
}
impl ToSql for SleepPlan {
fn fmt_sql(&self, f: &mut String, _fmt: surrealdb_types::SqlFormat) {
f.push_str("SLEEP ");
f.push_str(&self.duration.to_sql());
}
}
#[cfg(test)]
mod tests {
use super::*;
fn test_sleep_plan(duration: Duration) -> SleepPlan {
SleepPlan::new(duration)
}
#[test]
fn test_sleep_plan_name() {
let plan = test_sleep_plan(Duration(std::time::Duration::from_millis(100)));
assert_eq!(plan.name(), "Sleep");
}
#[test]
fn test_sleep_plan_attrs() {
let plan = test_sleep_plan(Duration(std::time::Duration::from_secs(1)));
let attrs = plan.attrs();
assert_eq!(attrs.len(), 1);
assert_eq!(attrs[0].0, "duration");
}
#[test]
fn test_sleep_plan_is_scalar() {
let plan = test_sleep_plan(Duration(std::time::Duration::from_millis(100)));
assert!(plan.is_scalar());
}
#[test]
fn test_sleep_plan_access_mode() {
let plan = test_sleep_plan(Duration(std::time::Duration::from_millis(100)));
assert_eq!(plan.access_mode(), AccessMode::ReadWrite);
}
}