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