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