surrealdb_core/sql/
closure.rs

1use super::{Ident, Kind};
2use crate::ctx::MutableContext;
3use crate::{ctx::Context, dbs::Options, doc::CursorDoc, err::Error, sql::value::Value};
4use reblessive::tree::Stk;
5use revision::revisioned;
6use serde::{Deserialize, Serialize};
7use std::fmt;
8
9pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Closure";
10
11#[revisioned(revision = 1)]
12#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
13#[serde(rename = "$surrealdb::private::sql::Closure")]
14#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
15#[non_exhaustive]
16pub struct Closure {
17	pub args: Vec<(Ident, Kind)>,
18	pub returns: Option<Kind>,
19	pub body: Value,
20}
21
22impl Closure {
23	pub(crate) async fn compute(
24		&self,
25		stk: &mut Stk,
26		ctx: &Context,
27		opt: &Options,
28		doc: Option<&CursorDoc>,
29		args: Vec<Value>,
30	) -> Result<Value, Error> {
31		let mut ctx = MutableContext::new_isolated(ctx);
32		for (i, (name, kind)) in self.args.iter().enumerate() {
33			match (kind, args.get(i)) {
34				(Kind::Option(_), None) => continue,
35				(_, None) => {
36					return Err(Error::InvalidArguments {
37						name: "ANONYMOUS".to_string(),
38						message: format!("Expected a value for ${}", name),
39					})
40				}
41				(kind, Some(val)) => {
42					if let Ok(val) = val.to_owned().coerce_to(kind) {
43						ctx.add_value(name.to_string(), val.into());
44					} else {
45						return Err(Error::InvalidArguments {
46							name: "ANONYMOUS".to_string(),
47							message: format!(
48								"Expected a value of type '{kind}' for argument ${}",
49								name
50							),
51						});
52					}
53				}
54			}
55		}
56
57		let ctx = ctx.freeze();
58		let result = self.body.compute(stk, &ctx, opt, doc).await?;
59		if let Some(returns) = &self.returns {
60			result.coerce_to(returns).map_err(|e| e.function_check_from_coerce("ANONYMOUS"))
61		} else {
62			Ok(result)
63		}
64	}
65}
66
67impl fmt::Display for Closure {
68	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
69		f.write_str("|")?;
70		for (i, (name, kind)) in self.args.iter().enumerate() {
71			if i > 0 {
72				f.write_str(", ")?;
73			}
74			write!(f, "${name}: ")?;
75			match kind {
76				k @ Kind::Either(_) => write!(f, "<{}>", k)?,
77				k => write!(f, "{}", k)?,
78			}
79		}
80		f.write_str("|")?;
81		if let Some(returns) = &self.returns {
82			write!(f, " -> {returns}")?;
83		}
84		write!(f, " {}", self.body)
85	}
86}