surrealdb_sql/statements/
foreach.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
use crate::ctx::Context;
use crate::dbs::{Options, Transaction};
use crate::doc::CursorDoc;
use crate::err::Error;
use crate::{block::Entry, Block, Param, Value};
use async_recursion::async_recursion;
use derive::Store;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display};

#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub struct ForeachStatement {
	pub param: Param,
	pub range: Value,
	pub block: Block,
}

impl ForeachStatement {
	/// Check if we require a writeable transaction
	pub(crate) fn writeable(&self) -> bool {
		self.range.writeable() || self.block.writeable()
	}
	/// Process this type returning a computed simple Value
	#[cfg_attr(not(target_arch = "wasm32"), async_recursion)]
	#[cfg_attr(target_arch = "wasm32", async_recursion(?Send))]
	pub(crate) async fn compute(
		&self,
		ctx: &Context<'_>,
		opt: &Options,
		txn: &Transaction,
		doc: Option<&'async_recursion CursorDoc<'_>>,
	) -> Result<Value, Error> {
		// Check the loop data
		match &self.range.compute(ctx, opt, txn, doc).await? {
			Value::Array(arr) => {
				// Loop over the values
				'foreach: for v in arr.iter() {
					// Duplicate context
					let mut ctx = Context::new(ctx);
					// Set the current parameter
					let key = self.param.0.to_raw();
					let val = v.compute(&ctx, opt, txn, doc).await?;
					ctx.add_value(key, val);
					// Loop over the code block statements
					for v in self.block.iter() {
						// Compute each block entry
						let res = match v {
							Entry::Set(v) => {
								let val = v.compute(&ctx, opt, txn, doc).await?;
								ctx.add_value(v.name.to_owned(), val);
								Ok(Value::None)
							}
							Entry::Value(v) => v.compute(&ctx, opt, txn, doc).await,
							Entry::Break(v) => v.compute(&ctx, opt, txn, doc).await,
							Entry::Continue(v) => v.compute(&ctx, opt, txn, doc).await,
							Entry::Foreach(v) => v.compute(&ctx, opt, txn, doc).await,
							Entry::Ifelse(v) => v.compute(&ctx, opt, txn, doc).await,
							Entry::Select(v) => v.compute(&ctx, opt, txn, doc).await,
							Entry::Create(v) => v.compute(&ctx, opt, txn, doc).await,
							Entry::Update(v) => v.compute(&ctx, opt, txn, doc).await,
							Entry::Delete(v) => v.compute(&ctx, opt, txn, doc).await,
							Entry::Relate(v) => v.compute(&ctx, opt, txn, doc).await,
							Entry::Insert(v) => v.compute(&ctx, opt, txn, doc).await,
							Entry::Define(v) => v.compute(&ctx, opt, txn, doc).await,
							Entry::Remove(v) => v.compute(&ctx, opt, txn, doc).await,
							Entry::Output(v) => {
								return v.compute(&ctx, opt, txn, doc).await;
							}
							Entry::Throw(v) => {
								return v.compute(&ctx, opt, txn, doc).await;
							}
						};
						// Catch any special errors
						match res {
							Err(Error::Continue) => continue 'foreach,
							Err(Error::Break) => return Ok(Value::None),
							Err(err) => return Err(err),
							_ => (),
						};
					}
				}
				// Ok all good
				Ok(Value::None)
			}
			v => Err(Error::InvalidStatementTarget {
				value: v.to_string(),
			}),
		}
	}
}

impl Display for ForeachStatement {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		write!(f, "FOR {} IN {} {}", self.param, self.range, self.block)
	}
}