shape_runtime/engine/
execution.rs1use super::types::{ExecutionMetrics, ExecutionResult, ExecutionType};
4use crate::type_schema::with_async_scope;
5use shape_ast::error::Result;
6use shape_ast::parser;
7
8impl super::ShapeEngine {
9 pub fn execute(
14 &mut self,
15 executor: &mut impl super::ProgramExecutor,
16 source: &str,
17 ) -> Result<ExecutionResult> {
18 if let Some(ctx) = self.runtime.persistent_context_mut() {
20 ctx.set_data_load_mode(crate::context::DataLoadMode::Sync);
21 }
22
23 self.execute_with_options(executor, source, false)
24 }
25
26 pub async fn execute_async(
43 &mut self,
44 executor: &mut impl super::ProgramExecutor,
45 source: &str,
46 ) -> Result<ExecutionResult> {
47 if let Some(ctx) = self.runtime.persistent_context_mut() {
49 ctx.set_data_load_mode(crate::context::DataLoadMode::Async);
50 }
51
52 let schema_registry = self.runtime.schema_registry_arc();
55 with_async_scope(schema_registry, self.execute_async_inner(executor, source)).await
56 }
57
58 async fn execute_async_inner(
59 &mut self,
60 executor: &mut impl super::ProgramExecutor,
61 source: &str,
62 ) -> Result<ExecutionResult> {
63 let start_time = std::time::Instant::now();
64
65 let parse_start = std::time::Instant::now();
67 let mut program = parser::parse_program(source)?;
68 let parse_time_ms = parse_start.elapsed().as_millis() as u64;
69
70 shape_ast::transform::desugar_program(&mut program);
72
73 let analysis_start = std::time::Instant::now();
74 let analysis_time_ms = analysis_start.elapsed().as_millis() as u64;
75
76 let has_cache = self
78 .runtime
79 .persistent_context()
80 .map(|ctx| ctx.has_data_cache())
81 .unwrap_or(false);
82
83 if has_cache {
84 let queries = self.extract_data_queries(&program)?;
86
87 if let Some(ctx) = self.runtime.persistent_context_mut() {
89 ctx.prefetch_data(queries).await?;
90 }
91 }
92
93 self.set_source(source);
95
96 let runtime_start = std::time::Instant::now();
98 let result = executor.execute_program(self, &program)?;
99 let runtime_time_ms = runtime_start.elapsed().as_millis() as u64;
100
101 let total_time_ms = start_time.elapsed().as_millis() as u64;
102 let memory_used_bytes = self.estimate_memory_usage();
103 let rows_processed = Some(self.default_data.row_count());
104 let messages = self.collect_messages();
105
106 Ok(ExecutionResult {
107 value: result.wire_value,
108 type_info: result.type_info,
109 execution_type: result.execution_type,
110 metrics: ExecutionMetrics {
111 execution_time_ms: total_time_ms,
112 parse_time_ms,
113 analysis_time_ms,
114 runtime_time_ms,
115 memory_used_bytes,
116 rows_processed,
117 },
118 messages,
119 content_json: result.content_json,
120 content_html: result.content_html,
121 content_terminal: result.content_terminal,
122 })
123 }
124
125 pub async fn execute_repl(
131 &mut self,
132 executor: &mut impl super::ProgramExecutor,
133 source: &str,
134 ) -> Result<ExecutionResult> {
135 if let Some(ctx) = self.runtime.persistent_context_mut() {
137 ctx.set_data_load_mode(crate::context::DataLoadMode::Async);
138 }
139
140 let schema_registry = self.runtime.schema_registry_arc();
143 with_async_scope(schema_registry, self.execute_repl_inner(executor, source)).await
144 }
145
146 async fn execute_repl_inner(
147 &mut self,
148 executor: &mut impl super::ProgramExecutor,
149 source: &str,
150 ) -> Result<ExecutionResult> {
151 let start_time = std::time::Instant::now();
152
153 let parse_start = std::time::Instant::now();
155 let mut program = parser::parse_program(source)?;
156 let parse_time_ms = parse_start.elapsed().as_millis() as u64;
157
158 shape_ast::transform::desugar_program(&mut program);
160
161 let analysis_start = std::time::Instant::now();
162 let analysis_time_ms = analysis_start.elapsed().as_millis() as u64;
163
164 let has_cache = self
166 .runtime
167 .persistent_context()
168 .map(|ctx| ctx.has_data_cache())
169 .unwrap_or(false);
170
171 if has_cache {
172 let queries = self.extract_data_queries(&program)?;
173 if let Some(ctx) = self.runtime.persistent_context_mut() {
174 ctx.prefetch_data(queries).await?;
175 }
176 }
177
178 self.runtime.load_program(&program, &self.default_data)?;
180
181 self.set_source(source);
183
184 let runtime_start = std::time::Instant::now();
186 let result = executor.execute_program(self, &program)?;
187 let runtime_time_ms = runtime_start.elapsed().as_millis() as u64;
188
189 let total_time_ms = start_time.elapsed().as_millis() as u64;
190 let memory_used_bytes = self.estimate_memory_usage();
191 let rows_processed = Some(self.default_data.row_count());
192 let messages = self.collect_messages();
193
194 Ok(ExecutionResult {
195 value: result.wire_value,
196 type_info: result.type_info,
197 execution_type: ExecutionType::Repl,
198 metrics: ExecutionMetrics {
199 execution_time_ms: total_time_ms,
200 parse_time_ms,
201 analysis_time_ms,
202 runtime_time_ms,
203 memory_used_bytes,
204 rows_processed,
205 },
206 messages,
207 content_json: result.content_json,
208 content_html: result.content_html,
209 content_terminal: result.content_terminal,
210 })
211 }
212
213 pub fn parse_and_analyze(&mut self, source: &str) -> Result<shape_ast::Program> {
218 let _scope = self.runtime.enter_schema_scope();
221
222 if let Some(ctx) = self.runtime.persistent_context_mut() {
223 ctx.reset_for_new_execution();
224 }
225 let mut program = parser::parse_program(source)?;
226 shape_ast::transform::desugar_program(&mut program);
227 self.set_source(source);
228 Ok(program)
229 }
230
231 pub(super) fn execute_with_options(
233 &mut self,
234 executor: &mut impl super::ProgramExecutor,
235 source: &str,
236 _is_stdlib: bool,
237 ) -> Result<ExecutionResult> {
238 let _scope = self.runtime.enter_schema_scope();
241
242 let start_time = std::time::Instant::now();
243
244 if let Some(ctx) = self.runtime.persistent_context_mut() {
246 ctx.reset_for_new_execution();
247 }
248
249 Self::check_deprecated_apis(source)?;
251
252 let parse_start = std::time::Instant::now();
254 let mut program = parser::parse_program(source)?;
255 let parse_time_ms = parse_start.elapsed().as_millis() as u64;
256
257 shape_ast::transform::desugar_program(&mut program);
259
260 let analysis_start = std::time::Instant::now();
261 let analysis_time_ms = analysis_start.elapsed().as_millis() as u64;
262
263 self.set_source(source);
265
266 let runtime_start = std::time::Instant::now();
268 let result = executor.execute_program(self, &program)?;
269 let runtime_time_ms = runtime_start.elapsed().as_millis() as u64;
270
271 let total_time_ms = start_time.elapsed().as_millis() as u64;
272
273 let memory_used_bytes = self.estimate_memory_usage();
275
276 let rows_processed = Some(self.default_data.row_count());
278
279 let messages = self.collect_messages();
281
282 Ok(ExecutionResult {
283 value: result.wire_value,
284 type_info: result.type_info,
285 execution_type: result.execution_type,
286 metrics: ExecutionMetrics {
287 execution_time_ms: total_time_ms,
288 parse_time_ms,
289 analysis_time_ms,
290 runtime_time_ms,
291 memory_used_bytes,
292 rows_processed,
293 },
294 messages,
295 content_json: result.content_json,
296 content_html: result.content_html,
297 content_terminal: result.content_terminal,
298 })
299 }
300
301 pub fn execute_repl_command(
303 &mut self,
304 executor: &mut impl super::ProgramExecutor,
305 command: &str,
306 ) -> Result<ExecutionResult> {
307 let mut result = self.execute(executor, command)?;
308 result.execution_type = ExecutionType::Repl;
309 Ok(result)
310 }
311
312 fn check_deprecated_apis(source: &str) -> Result<()> {
316 let trimmed = source.trim();
317 if trimmed.starts_with("csv.load") || trimmed.contains("csv.load(") {
318 return Err(shape_ast::error::ShapeError::SemanticError {
319 message: "csv.load has been removed. Use the csv package instead: import { read } from \"csv\""
320 .to_string(),
321 location: None,
322 });
323 }
324 if trimmed.starts_with("load(") || trimmed.starts_with("load (") {
326 return Err(shape_ast::error::ShapeError::SemanticError {
327 message: "load(provider, params) has been removed. Use typed data access instead: data(\"source\", { ... })"
328 .to_string(),
329 location: None,
330 });
331 }
332 Ok(())
333 }
334
335 pub(super) fn estimate_memory_usage(&self) -> Option<usize> {
337 let mut total = 0usize;
339
340 total += self.default_data.row_count() * 48;
342
343 total += 1024; Some(total)
348 }
349
350 pub(super) fn collect_messages(&self) -> Vec<super::types::Message> {
352 vec![]
355 }
356}