surrealdb-core 3.3.1

A scalable, distributed, collaborative, document-graph database, for the realtime web
#[cfg(feature = "surrealism")]
use std::sync::Arc;

use anyhow::{Result, bail};
use reblessive::tree::Stk;

use crate::catalog::{DatabaseId, NamespaceId};
use crate::ctx::FrozenContext;
use crate::dbs::Options;
#[cfg(feature = "surrealism")]
use crate::dbs::capabilities::ExperimentalTarget;
use crate::doc::CursorDoc;
use crate::expr::module::{ModuleExecutable, Signature, SiloExecutable, SurrealismExecutable};
#[cfg(feature = "surrealism")]
use crate::surrealism::cache::SurrealismCacheLookup;
#[cfg(feature = "surrealism")]
use crate::surrealism::cache::SurrealismCachedModule;
#[cfg(feature = "surrealism")]
use crate::surrealism::host::Host;
use crate::val::Value;

pub(crate) async fn module_executable_signature(
	this: &ModuleExecutable,
	ctx: &FrozenContext,
	ns: &NamespaceId,
	db: &DatabaseId,
	sub: Option<&str>,
) -> Result<Signature> {
	match this {
		ModuleExecutable::Surrealism(surrealism) => {
			crate::legacy::surrealism_executable_signature(surrealism, ctx, ns, db, sub).await
		}
		ModuleExecutable::Silo(silo) => {
			crate::legacy::silo_executable_signature(silo, ctx, ns, db, sub).await
		}
	}
}

pub(crate) async fn module_executable_run(
	this: &ModuleExecutable,
	stk: &mut Stk,
	ctx: &FrozenContext,
	opt: &Options,
	doc: Option<&CursorDoc>,
	args: Vec<Value>,
	sub: Option<&str>,
) -> Result<Value> {
	match this {
		ModuleExecutable::Surrealism(surrealism) => {
			crate::legacy::surrealism_executable_run(surrealism, stk, ctx, opt, doc, args, sub)
				.await
		}
		ModuleExecutable::Silo(silo) => {
			crate::legacy::silo_executable_run(silo, stk, ctx, opt, doc, args, sub).await
		}
	}
}

#[cfg(feature = "surrealism")]
pub(crate) async fn surrealism_executable_signature(
	this: &SurrealismExecutable,
	ctx: &FrozenContext,
	ns: &NamespaceId,
	db: &DatabaseId,
	sub: Option<&str>,
) -> Result<Signature> {
	check_surrealism_enabled(ctx)?;
	let lookup = SurrealismCacheLookup::File(ns, db, &this.0.bucket, &this.0.key);
	let runtime = ctx.get_surrealism_runtime(lookup).await?;
	signature_from_runtime(&runtime, sub)
}

#[cfg(feature = "surrealism")]
pub(crate) async fn surrealism_executable_run(
	this: &SurrealismExecutable,
	_stk: &mut Stk,
	ctx: &FrozenContext,
	opt: &Options,
	doc: Option<&CursorDoc>,
	args: Vec<Value>,
	sub: Option<&str>,
) -> Result<Value> {
	check_surrealism_enabled(ctx)?;
	let (ns, db) = ctx.get_ns_db_ids(opt).await?;
	let lookup = SurrealismCacheLookup::File(&ns, &db, &this.0.bucket, &this.0.key);
	let cached = ctx.get_surrealism_module(lookup).await?;
	run_on_runtime(cached, ctx, opt, doc, args, sub).await
}

#[cfg(not(feature = "surrealism"))]
pub(crate) async fn surrealism_executable_signature(
	_this: &SurrealismExecutable,
	_ctx: &FrozenContext,
	_ns: &NamespaceId,
	_db: &DatabaseId,
	_sub: Option<&str>,
) -> Result<Signature> {
	bail!("Surrealism modules are not supported in WASM environments")
}

#[cfg(not(feature = "surrealism"))]
pub(crate) async fn surrealism_executable_run(
	_this: &SurrealismExecutable,
	_stk: &mut Stk,
	_ctx: &FrozenContext,
	_opt: &Options,
	_doc: Option<&CursorDoc>,
	_args: Vec<Value>,
	_sub: Option<&str>,
) -> Result<Value> {
	bail!("Surrealism functions are not supported in WASM environments")
}

#[cfg(feature = "surrealism")]
pub(crate) async fn silo_executable_signature(
	this: &SiloExecutable,
	ctx: &FrozenContext,
	ns: &NamespaceId,
	db: &DatabaseId,
	sub: Option<&str>,
) -> Result<Signature> {
	check_surrealism_enabled(ctx)?;
	let lookup = SurrealismCacheLookup::Silo(
		ns,
		db,
		&this.organisation,
		&this.package,
		this.major,
		this.minor,
		this.patch,
	);
	let runtime = ctx.get_surrealism_runtime(lookup).await?;
	signature_from_runtime(&runtime, sub)
}

#[cfg(feature = "surrealism")]
pub(crate) async fn silo_executable_run(
	this: &SiloExecutable,
	_stk: &mut Stk,
	ctx: &FrozenContext,
	opt: &Options,
	doc: Option<&CursorDoc>,
	args: Vec<Value>,
	sub: Option<&str>,
) -> Result<Value> {
	check_surrealism_enabled(ctx)?;
	let (ns, db) = ctx.get_ns_db_ids(opt).await?;
	let lookup = SurrealismCacheLookup::Silo(
		&ns,
		&db,
		&this.organisation,
		&this.package,
		this.major,
		this.minor,
		this.patch,
	);
	let cached = ctx.get_surrealism_module(lookup).await?;
	run_on_runtime(cached, ctx, opt, doc, args, sub).await
}

#[cfg(not(feature = "surrealism"))]
pub(crate) async fn silo_executable_signature(
	_this: &SiloExecutable,
	_ctx: &FrozenContext,
	_ns: &NamespaceId,
	_db: &DatabaseId,
	_sub: Option<&str>,
) -> Result<Signature> {
	bail!("Surrealism functions are not supported in WASM environments")
}

#[cfg(not(feature = "surrealism"))]
pub(crate) async fn silo_executable_run(
	_this: &SiloExecutable,
	_stk: &mut Stk,
	_ctx: &FrozenContext,
	_opt: &Options,
	_doc: Option<&CursorDoc>,
	_args: Vec<Value>,
	_sub: Option<&str>,
) -> Result<Value> {
	bail!("Surrealism functions are not supported in WASM environments")
}

#[cfg(feature = "surrealism")]
pub(crate) fn check_surrealism_enabled(ctx: &FrozenContext) -> Result<()> {
	if !ctx.get_capabilities().allows_experimental(&ExperimentalTarget::Surrealism) {
		bail!("Experimental capability `surrealism` is not enabled");
	}
	Ok(())
}

#[cfg(feature = "surrealism")]
pub(crate) async fn run_on_runtime(
	cached: SurrealismCachedModule,
	ctx: &FrozenContext,
	opt: &Options,
	doc: Option<&CursorDoc>,
	args: Vec<Value>,
	sub: Option<&str>,
) -> Result<Value> {
	let display_name = sub.unwrap_or("<default>");
	tracing::debug!(name = %display_name, arg_count = args.len(), "run_on_runtime: starting");

	let args: Result<Vec<crate::types::PublicValue>, _> =
		args.into_iter().map(|x| x.try_into()).collect();
	let args = args?;

	let SurrealismCachedModule {
		runtime,
		module_display_name,
		#[cfg(feature = "http")]
		client,
	} = cached;
	let module_name = module_display_name.as_ref().to_string();
	let host = Box::new(Host::new(
		ctx,
		opt,
		doc,
		Arc::clone(runtime.kv_store()),
		module_name,
		#[cfg(feature = "http")]
		client,
	));
	let mut controller = runtime.acquire_controller(host).await?;

	let ctx_timeout = ctx.timeout();
	let result = controller.invoke_with_timeout(sub.map(String::from), args, ctx_timeout).await;

	if result.as_ref().is_err_and(|e| e.is_trap()) {
		tracing::error!(
			name = %display_name,
			error = ?result.as_ref().err(),
			"run_on_runtime: WASM TRAP, dropping controller"
		);
		drop(controller);
	} else {
		runtime.release_controller(controller);
	}

	Ok(result?.into())
}

#[cfg(feature = "surrealism")]
pub(crate) fn signature_from_runtime(
	runtime: &surrealism_runtime::runtime::Runtime,
	sub: Option<&str>,
) -> Result<Signature> {
	let export = runtime.get_signature(sub)?;
	Ok(Signature {
		args: export.args.iter().map(|(_, k)| k.clone().into()).collect(),
		returns: Some(export.returns.clone().into()),
		writeable: export.writeable,
	})
}