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