armature_rhai/
context.rs

1//! Script execution context.
2
3use crate::bindings::{RequestBinding, ResponseBinding};
4use rhai::{Dynamic, Map, Scope};
5use std::collections::HashMap;
6use std::sync::Arc;
7
8/// Context for script execution.
9///
10/// Provides access to request data, shared state, and utilities.
11#[derive(Debug, Clone)]
12pub struct ScriptContext {
13    /// Request binding.
14    pub request: RequestBinding,
15    /// Shared state accessible to scripts.
16    pub state: Arc<HashMap<String, Dynamic>>,
17    /// Request-scoped data.
18    pub locals: HashMap<String, Dynamic>,
19}
20
21impl ScriptContext {
22    /// Create a new script context.
23    pub fn new(request: RequestBinding) -> Self {
24        Self {
25            request,
26            state: Arc::new(HashMap::new()),
27            locals: HashMap::new(),
28        }
29    }
30
31    /// Create context with shared state.
32    pub fn with_state(request: RequestBinding, state: Arc<HashMap<String, Dynamic>>) -> Self {
33        Self {
34            request,
35            state,
36            locals: HashMap::new(),
37        }
38    }
39
40    /// Set a local value.
41    pub fn set_local(&mut self, key: impl Into<String>, value: Dynamic) {
42        self.locals.insert(key.into(), value);
43    }
44
45    /// Get a local value.
46    pub fn get_local(&self, key: &str) -> Option<&Dynamic> {
47        self.locals.get(key)
48    }
49
50    /// Get a state value.
51    pub fn get_state(&self, key: &str) -> Option<&Dynamic> {
52        self.state.get(key)
53    }
54
55    /// Build a Rhai scope from this context.
56    pub fn into_scope(self) -> Scope<'static> {
57        let mut scope = Scope::new();
58
59        // Add request
60        scope.push_constant("request", self.request);
61
62        // Add response builder (for chaining)
63        scope.push("response", ResponseBinding::new());
64
65        // Add state as a map
66        let mut state_map = Map::new();
67        for (key, value) in self.state.iter() {
68            state_map.insert(key.clone().into(), value.clone());
69        }
70        scope.push_constant("state", state_map);
71
72        // Add locals
73        for (key, value) in self.locals {
74            scope.push(&key, value);
75        }
76
77        scope
78    }
79}
80
81/// Builder for creating script contexts.
82pub struct ScriptContextBuilder {
83    request: Option<RequestBinding>,
84    state: HashMap<String, Dynamic>,
85    locals: HashMap<String, Dynamic>,
86}
87
88impl Default for ScriptContextBuilder {
89    fn default() -> Self {
90        Self::new()
91    }
92}
93
94impl ScriptContextBuilder {
95    /// Create a new builder.
96    pub fn new() -> Self {
97        Self {
98            request: None,
99            state: HashMap::new(),
100            locals: HashMap::new(),
101        }
102    }
103
104    /// Set the request.
105    pub fn request(mut self, request: RequestBinding) -> Self {
106        self.request = Some(request);
107        self
108    }
109
110    /// Add a state value.
111    pub fn state(mut self, key: impl Into<String>, value: impl Into<Dynamic>) -> Self {
112        self.state.insert(key.into(), value.into());
113        self
114    }
115
116    /// Add a local value.
117    pub fn local(mut self, key: impl Into<String>, value: impl Into<Dynamic>) -> Self {
118        self.locals.insert(key.into(), value.into());
119        self
120    }
121
122    /// Build the context.
123    pub fn build(self) -> Option<ScriptContext> {
124        let request = self.request?;
125        Some(ScriptContext {
126            request,
127            state: Arc::new(self.state),
128            locals: self.locals,
129        })
130    }
131}
132