1use std::sync::Arc;
2
3use anyhow::Result;
4use indexmap::IndexMap;
5use kittycad_modeling_cmds::websocket::WebSocketResponse;
6use schemars::JsonSchema;
7use serde::{Deserialize, Serialize};
8use uuid::Uuid;
9
10use crate::{
11 errors::{KclError, KclErrorDetails, Severity},
12 execution::{
13 annotations,
14 id_generator::IdGenerator,
15 memory::{ProgramMemory, Stack},
16 types, Artifact, ArtifactCommand, ArtifactGraph, ArtifactId, EnvironmentRef, ExecOutcome, ExecutorSettings,
17 KclValue, Operation, UnitAngle, UnitLen,
18 },
19 modules::{ModuleId, ModuleInfo, ModuleLoader, ModulePath, ModuleRepr, ModuleSource},
20 parsing::ast::types::Annotation,
21 source_range::SourceRange,
22 CompilationError,
23};
24
25#[derive(Debug, Clone)]
27pub struct ExecState {
28 pub(super) global: GlobalState,
29 pub(super) mod_local: ModuleState,
30 pub(super) exec_context: Option<super::ExecutorContext>,
31}
32
33#[derive(Debug, Clone)]
34pub(super) struct GlobalState {
35 pub path_to_source_id: IndexMap<ModulePath, ModuleId>,
37 pub id_to_source: IndexMap<ModuleId, ModuleSource>,
39 pub module_infos: IndexMap<ModuleId, ModuleInfo>,
41 pub artifacts: IndexMap<ArtifactId, Artifact>,
43 pub artifact_commands: Vec<ArtifactCommand>,
47 pub artifact_responses: IndexMap<Uuid, WebSocketResponse>,
52 pub artifact_graph: ArtifactGraph,
54 pub operations: Vec<Operation>,
57 pub mod_loader: ModuleLoader,
59 pub errors: Vec<CompilationError>,
61}
62
63#[derive(Debug, Clone)]
64pub(super) struct ModuleState {
65 pub id_generator: IdGenerator,
67 pub stack: Stack,
68 pub pipe_value: Option<KclValue>,
71 pub module_exports: Vec<String>,
73 pub settings: MetaSettings,
75 pub(super) explicit_length_units: bool,
76 pub(super) std_path: Option<String>,
77}
78
79impl ExecState {
80 pub fn new(exec_context: &super::ExecutorContext) -> Self {
81 ExecState {
82 global: GlobalState::new(&exec_context.settings),
83 mod_local: ModuleState::new(&exec_context.settings, None, ProgramMemory::new(), Default::default()),
84 exec_context: Some(exec_context.clone()),
85 }
86 }
87
88 pub(super) fn reset(&mut self, exec_context: &super::ExecutorContext) {
89 let global = GlobalState::new(&exec_context.settings);
90
91 *self = ExecState {
92 global,
93 mod_local: ModuleState::new(&exec_context.settings, None, ProgramMemory::new(), Default::default()),
94 exec_context: Some(exec_context.clone()),
95 };
96 }
97
98 pub fn err(&mut self, e: CompilationError) {
100 self.global.errors.push(e);
101 }
102
103 pub fn warn(&mut self, mut e: CompilationError) {
105 e.severity = Severity::Warning;
106 self.global.errors.push(e);
107 }
108
109 pub fn errors(&self) -> &[CompilationError] {
110 &self.global.errors
111 }
112
113 pub async fn to_wasm_outcome(self, main_ref: EnvironmentRef) -> ExecOutcome {
117 ExecOutcome {
120 variables: self
121 .stack()
122 .find_all_in_env(main_ref)
123 .map(|(k, v)| (k.clone(), v.clone()))
124 .collect(),
125 operations: self.global.operations,
126 artifact_commands: self.global.artifact_commands,
127 artifact_graph: self.global.artifact_graph,
128 errors: self.global.errors,
129 filenames: self
130 .global
131 .path_to_source_id
132 .iter()
133 .map(|(k, v)| ((*v), k.clone()))
134 .collect(),
135 default_planes: if let Some(ctx) = &self.exec_context {
136 ctx.engine.get_default_planes().read().await.clone()
137 } else {
138 None
139 },
140 }
141 }
142
143 pub async fn to_mock_wasm_outcome(self, main_ref: EnvironmentRef) -> ExecOutcome {
144 ExecOutcome {
147 variables: self
148 .stack()
149 .find_all_in_env(main_ref)
150 .map(|(k, v)| (k.clone(), v.clone()))
151 .collect(),
152 operations: Default::default(),
153 artifact_commands: Default::default(),
154 artifact_graph: Default::default(),
155 errors: self.global.errors,
156 filenames: Default::default(),
157 default_planes: if let Some(ctx) = &self.exec_context {
158 ctx.engine.get_default_planes().read().await.clone()
159 } else {
160 None
161 },
162 }
163 }
164
165 pub(crate) fn stack(&self) -> &Stack {
166 &self.mod_local.stack
167 }
168
169 pub(crate) fn mut_stack(&mut self) -> &mut Stack {
170 &mut self.mod_local.stack
171 }
172
173 pub fn next_uuid(&mut self) -> Uuid {
174 self.mod_local.id_generator.next_uuid()
175 }
176
177 pub fn id_generator(&mut self) -> &mut IdGenerator {
178 &mut self.mod_local.id_generator
179 }
180
181 pub(crate) fn add_artifact(&mut self, artifact: Artifact) {
182 let id = artifact.id();
183 self.global.artifacts.insert(id, artifact);
184 }
185
186 pub(super) fn next_module_id(&self) -> ModuleId {
187 ModuleId::from_usize(self.global.path_to_source_id.len())
188 }
189
190 pub(super) fn id_for_module(&self, path: &ModulePath) -> Option<ModuleId> {
191 self.global.path_to_source_id.get(path).cloned()
192 }
193
194 pub(super) fn add_path_to_source_id(&mut self, path: ModulePath, id: ModuleId) {
195 debug_assert!(!self.global.path_to_source_id.contains_key(&path));
196 self.global.path_to_source_id.insert(path.clone(), id);
197 }
198
199 pub(crate) fn add_root_module_contents(&mut self, program: &crate::Program) {
200 let root_id = ModuleId::default();
201 let path = self
203 .global
204 .path_to_source_id
205 .iter()
206 .find(|(_, v)| **v == root_id)
207 .unwrap()
208 .0
209 .clone();
210 self.add_id_to_source(
211 root_id,
212 ModuleSource {
213 path,
214 source: program.original_file_contents.to_string(),
215 },
216 );
217 }
218
219 pub(super) fn add_id_to_source(&mut self, id: ModuleId, source: ModuleSource) {
220 self.global.id_to_source.insert(id, source.clone());
221 }
222
223 pub(super) fn add_module(&mut self, id: ModuleId, path: ModulePath, repr: ModuleRepr) {
224 debug_assert!(self.global.path_to_source_id.contains_key(&path));
225 let module_info = ModuleInfo { id, repr, path };
226 self.global.module_infos.insert(id, module_info);
227 }
228
229 pub fn length_unit(&self) -> UnitLen {
230 self.mod_local.settings.default_length_units
231 }
232
233 pub fn angle_unit(&self) -> UnitAngle {
234 self.mod_local.settings.default_angle_units
235 }
236
237 pub(super) fn circular_import_error(&self, path: &ModulePath, source_range: SourceRange) -> KclError {
238 KclError::ImportCycle(KclErrorDetails {
239 message: format!(
240 "circular import of modules is not allowed: {} -> {}",
241 self.global
242 .mod_loader
243 .import_stack
244 .iter()
245 .map(|p| p.as_path().to_string_lossy())
246 .collect::<Vec<_>>()
247 .join(" -> "),
248 path,
249 ),
250 source_ranges: vec![source_range],
251 })
252 }
253}
254
255impl GlobalState {
256 fn new(settings: &ExecutorSettings) -> Self {
257 let mut global = GlobalState {
258 path_to_source_id: Default::default(),
259 module_infos: Default::default(),
260 artifacts: Default::default(),
261 artifact_commands: Default::default(),
262 artifact_responses: Default::default(),
263 artifact_graph: Default::default(),
264 operations: Default::default(),
265 mod_loader: Default::default(),
266 errors: Default::default(),
267 id_to_source: Default::default(),
268 };
269
270 let root_id = ModuleId::default();
271 let root_path = settings.current_file.clone().unwrap_or_default();
272 global.module_infos.insert(
273 root_id,
274 ModuleInfo {
275 id: root_id,
276 path: ModulePath::Local {
277 value: root_path.clone(),
278 },
279 repr: ModuleRepr::Root,
280 },
281 );
282 global
283 .path_to_source_id
284 .insert(ModulePath::Local { value: root_path }, root_id);
285 global
286 }
287}
288
289impl ModuleState {
290 pub(super) fn new(
291 exec_settings: &ExecutorSettings,
292 std_path: Option<String>,
293 memory: Arc<ProgramMemory>,
294 module_id: Option<ModuleId>,
295 ) -> Self {
296 ModuleState {
297 id_generator: IdGenerator::new(module_id),
298 stack: memory.new_stack(),
299 pipe_value: Default::default(),
300 module_exports: Default::default(),
301 explicit_length_units: false,
302 std_path,
303 settings: MetaSettings {
304 default_length_units: exec_settings.units.into(),
305 default_angle_units: Default::default(),
306 },
307 }
308 }
309}
310
311#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
312#[ts(export)]
313#[serde(rename_all = "camelCase")]
314pub struct MetaSettings {
315 pub default_length_units: types::UnitLen,
316 pub default_angle_units: types::UnitAngle,
317}
318
319impl MetaSettings {
320 pub(crate) fn update_from_annotation(
321 &mut self,
322 annotation: &crate::parsing::ast::types::Node<Annotation>,
323 ) -> Result<bool, KclError> {
324 let properties = annotations::expect_properties(annotations::SETTINGS, annotation)?;
325
326 let mut updated_len = false;
327 for p in properties {
328 match &*p.inner.key.name {
329 annotations::SETTINGS_UNIT_LENGTH => {
330 let value = annotations::expect_ident(&p.inner.value)?;
331 let value = types::UnitLen::from_str(value, annotation.as_source_range())?;
332 self.default_length_units = value;
333 updated_len = true;
334 }
335 annotations::SETTINGS_UNIT_ANGLE => {
336 let value = annotations::expect_ident(&p.inner.value)?;
337 let value = types::UnitAngle::from_str(value, annotation.as_source_range())?;
338 self.default_angle_units = value;
339 }
340 name => {
341 return Err(KclError::Semantic(KclErrorDetails {
342 message: format!(
343 "Unexpected settings key: `{name}`; expected one of `{}`, `{}`",
344 annotations::SETTINGS_UNIT_LENGTH,
345 annotations::SETTINGS_UNIT_ANGLE
346 ),
347 source_ranges: vec![annotation.as_source_range()],
348 }))
349 }
350 }
351 }
352
353 Ok(updated_len)
354 }
355}