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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
//! The Rhai engine: `engine()`/`engine_with_limits()` (the shared DSL registrations of
//! `register/`, then the per-engine limits, expression depths and keyword token rewriting) and
//! `registered_step_names()`.
use *;
/// Maximum expression nesting depth of a script (Rhai counts every call of a method chain and every
/// nested call). 128 is twice Rhai's release default and four times its debug default, enough for
/// the longest TinkerPop feature queries. It is not higher because the parser recurses: on a 2 MiB
/// thread stack (the default of spawned threads) a debug build overflowed the stack at 160/80,
/// while 128/64 rejects a too-long chain cleanly. Identical in every build profile.
const MAX_EXPR_DEPTH: usize = 128;
/// Maximum expression depth inside a script-defined function body; half of [`MAX_EXPR_DEPTH`].
const MAX_FUNCTION_EXPR_DEPTH: usize = 64;
/// Builds the Rhai engine with every DSL type/step registered. Shared by [`eval_value`] and its
/// parameterized variants. Cheap (microseconds): the registrations and Rhai's standard library
/// are built once per thread and attached to each new engine by pointer; only the first engine
/// of a thread pays for building them (milliseconds).
///
/// The engine carries the SAFE [`ScriptLimits::default`] limits (operations, string, array, map
/// size, recursion depth), so a script cannot hang or exhaust the process, and allows
/// everything ([`AllowAll`](crate::graph::AllowAll)): `import` loads modules and the graphs a
/// script builds itself can be read and written. Use [`engine_with_limits`] to choose both, for
/// example an [`AccessPolicy`](crate::graph::AccessPolicy) for scripts the host did not write.
/// [`engine`] with explicit [`ScriptLimits`] and [`Authorizer`](crate::graph::Authorizer).
/// Every limit hit surfaces as an
/// `EvalAltResult` that [`resource_limit_from_rhai`] maps to
/// [`TraverserError::ResourceLimitExceeded`](crate::TraverserError::ResourceLimitExceeded).
///
/// The authorizer governs what the engine itself provides: Rhai's `import "module"` (`Execute`
/// on the module's `File`, resolved by [`Authorizer::resolve_file`](crate::graph::Authorizer::resolve_file),
/// e.g. under a file root) and the traversal sources a script creates (`GraphSource::empty()`,
/// `GraphSource::file(path)`, `schema.toGraph()`). The session's own `g` gets its authorizer
/// from [`graph_scope_with_options`]: pass the same one to both.
/// [`engine_with_limits`] with the host's preset traversal `options` for the traversal sources
/// the engine itself provides: `GraphSource::empty()`, `GraphSource::tinkerpop_modern()`,
/// `GraphSource::file(path)`, `schema.toGraph()`, `subgraph(..)` results. Pass the same options
/// as to [`graph_scope_with_options`] (which covers `g`, `_g` and `__`), so a script cannot escape
/// a preset such as `evaluationTimeout` by creating a source of its own:
///
/// ```rust
/// # use std::sync::Arc;
/// # use graphersal::prelude::*;
/// # use graphersal::auth::{AllowAll, Authorizer};
/// let authorizer: Arc<dyn Authorizer> = Arc::new(AllowAll);
/// let options = [("evaluationTimeout", ElementProperty::from(1))];
/// let engine =
/// graphersal::script::engine_with_options(&Default::default(), authorizer.clone(), &options);
/// let mut scope = graphersal::script::graph_scope_with_options(
/// Arc::new(GraphSource::tinkerpop_modern()),
/// &options,
/// authorizer,
/// );
/// let timed_out = engine.eval_with_scope::<graphersal::script::rhai::Dynamic>(
/// &mut scope,
/// "GraphSource::empty().inject(1).repeat(__.group()).times(60).count().next()",
/// );
/// assert!(timed_out.unwrap_err().to_string().contains("evaluationTimeout"));
/// ```
/// Every step/method name registered with the Rhai engine, sorted and deduplicated. Used to keep
/// documentation/tests in sync with the DSL's actual registered surface.