1use std::path::PathBuf;
4
5use crate::{
6 engine::new_zoo_client,
7 errors::ExecErrorWithState,
8 execution::{EnvironmentRef, ExecState, ExecutorContext, ExecutorSettings},
9 settings::types::UnitLength,
10 ConnectionError, ExecError, KclError, KclErrorWithOutputs, Program,
11};
12
13#[derive(serde::Deserialize, serde::Serialize)]
14pub struct RequestBody {
15 pub kcl_program: String,
16 #[serde(default)]
17 pub test_name: String,
18}
19
20pub async fn execute_and_snapshot(
23 code: &str,
24 units: UnitLength,
25 current_file: Option<PathBuf>,
26) -> Result<image::DynamicImage, ExecError> {
27 let ctx = new_context(units, true, current_file).await?;
28 let program = Program::parse_no_errs(code).map_err(KclErrorWithOutputs::no_outputs)?;
29 let res = do_execute_and_snapshot(&ctx, program)
30 .await
31 .map(|(_, _, snap)| snap)
32 .map_err(|err| err.error);
33 ctx.close().await;
34 res
35}
36
37pub async fn execute_and_snapshot_ast(
40 ast: Program,
41 units: UnitLength,
42 current_file: Option<PathBuf>,
43) -> Result<(ExecState, EnvironmentRef, image::DynamicImage), ExecErrorWithState> {
44 let ctx = new_context(units, true, current_file).await?;
45 let res = do_execute_and_snapshot(&ctx, ast).await;
46 ctx.close().await;
47 res
48}
49
50pub async fn execute_and_snapshot_no_auth(
51 code: &str,
52 units: UnitLength,
53 current_file: Option<PathBuf>,
54) -> Result<(image::DynamicImage, EnvironmentRef), ExecError> {
55 let ctx = new_context(units, false, current_file).await?;
56 let program = Program::parse_no_errs(code).map_err(KclErrorWithOutputs::no_outputs)?;
57 let res = do_execute_and_snapshot(&ctx, program)
58 .await
59 .map(|(_, env_ref, snap)| (snap, env_ref))
60 .map_err(|err| err.error);
61 ctx.close().await;
62 res
63}
64
65async fn do_execute_and_snapshot(
66 ctx: &ExecutorContext,
67 program: Program,
68) -> Result<(ExecState, EnvironmentRef, image::DynamicImage), ExecErrorWithState> {
69 let mut exec_state = ExecState::new(&ctx.settings);
70 let result = ctx
71 .run_with_ui_outputs(&program, &mut exec_state)
72 .await
73 .map_err(|err| ExecErrorWithState::new(err.into(), exec_state.clone()))?;
74 for e in exec_state.errors() {
75 if e.severity.is_err() {
76 return Err(ExecErrorWithState::new(
77 KclErrorWithOutputs::no_outputs(KclError::Semantic(e.clone().into())).into(),
78 exec_state.clone(),
79 ));
80 }
81 }
82 let snapshot_png_bytes = ctx
83 .prepare_snapshot()
84 .await
85 .map_err(|err| ExecErrorWithState::new(err, exec_state.clone()))?
86 .contents
87 .0;
88
89 let img = image::ImageReader::new(std::io::Cursor::new(snapshot_png_bytes))
91 .with_guessed_format()
92 .map_err(|e| ExecError::BadPng(e.to_string()))
93 .and_then(|x| x.decode().map_err(|e| ExecError::BadPng(e.to_string())))
94 .map_err(|err| ExecErrorWithState::new(err, exec_state.clone()))?;
95
96 ctx.close().await;
97
98 Ok((exec_state, result.0, img))
99}
100
101pub async fn new_context(
102 units: UnitLength,
103 with_auth: bool,
104 current_file: Option<PathBuf>,
105) -> Result<ExecutorContext, ConnectionError> {
106 let mut client = new_zoo_client(if with_auth { None } else { Some("bad_token".to_string()) }, None)
107 .map_err(ConnectionError::CouldNotMakeClient)?;
108 if !with_auth {
109 client.set_base_url("https://api.zoo.dev".to_string());
113 }
114
115 let mut settings = ExecutorSettings {
116 units,
117 highlight_edges: true,
118 enable_ssao: false,
119 show_grid: false,
120 replay: None,
121 project_directory: None,
122 current_file: None,
123 };
124 if let Some(current_file) = current_file {
125 settings.with_current_file(current_file);
126 }
127 let ctx = ExecutorContext::new(&client, settings)
128 .await
129 .map_err(ConnectionError::Establishing)?;
130 Ok(ctx)
131}