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(super) fn add_id_to_source(&mut self, id: ModuleId, source: ModuleSource) {
186 debug_assert!(!self.global.id_to_source.contains_key(&id));
187 self.global.id_to_source.insert(id, source.clone());
188 }
189
190 pub(super) fn add_module(&mut self, id: ModuleId, path: ModulePath, repr: ModuleRepr) {
191 debug_assert!(self.global.path_to_source_id.contains_key(&path));
192 let module_info = ModuleInfo { id, repr, path };
193 self.global.module_infos.insert(id, module_info);
194 }
195
196 pub fn length_unit(&self) -> UnitLen {
197 self.mod_local.settings.default_length_units
198 }
199
200 pub fn angle_unit(&self) -> UnitAngle {
201 self.mod_local.settings.default_angle_units
202 }
203
204 pub(super) fn circular_import_error(&self, path: &ModulePath, source_range: SourceRange) -> KclError {
205 KclError::ImportCycle(KclErrorDetails {
206 message: format!(
207 "circular import of modules is not allowed: {} -> {}",
208 self.global
209 .mod_loader
210 .import_stack
211 .iter()
212 .map(|p| p.as_path().to_string_lossy())
213 .collect::<Vec<_>>()
214 .join(" -> "),
215 path,
216 ),
217 source_ranges: vec![source_range],
218 })
219 }
220}
221
222impl GlobalState {
223 fn new(settings: &ExecutorSettings) -> Self {
224 let mut global = GlobalState {
225 memory: ProgramMemory::new(),
226 id_generator: Default::default(),
227 path_to_source_id: Default::default(),
228 module_infos: Default::default(),
229 artifacts: Default::default(),
230 artifact_commands: Default::default(),
231 artifact_responses: Default::default(),
232 artifact_graph: Default::default(),
233 operations: Default::default(),
234 mod_loader: Default::default(),
235 errors: Default::default(),
236 id_to_source: Default::default(),
237 };
238
239 let root_id = ModuleId::default();
240 let root_path = settings.current_file.clone().unwrap_or_default();
241 global.module_infos.insert(
242 root_id,
243 ModuleInfo {
244 id: root_id,
245 path: ModulePath::Local {
246 value: root_path.clone(),
247 },
248 repr: ModuleRepr::Root,
249 },
250 );
251 global
252 .path_to_source_id
253 .insert(ModulePath::Local { value: root_path }, root_id);
254 global
257 }
258}
259
260impl ModuleState {
261 pub(super) fn new(exec_settings: &ExecutorSettings, std_path: Option<String>) -> Self {
262 ModuleState {
263 pipe_value: Default::default(),
264 module_exports: Default::default(),
265 settings: MetaSettings {
266 default_length_units: exec_settings.units.into(),
267 default_angle_units: Default::default(),
268 std_path,
269 },
270 }
271 }
272}
273
274#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
275#[ts(export)]
276#[serde(rename_all = "camelCase")]
277pub struct MetaSettings {
278 pub default_length_units: kcl_value::UnitLen,
279 pub default_angle_units: kcl_value::UnitAngle,
280 pub std_path: Option<String>,
281}
282
283impl MetaSettings {
284 pub(crate) fn update_from_annotation(
285 &mut self,
286 annotation: &crate::parsing::ast::types::Node<Annotation>,
287 ) -> Result<(), KclError> {
288 let properties = annotations::expect_properties(annotations::SETTINGS, annotation)?;
289
290 for p in properties {
291 match &*p.inner.key.name {
292 annotations::SETTINGS_UNIT_LENGTH => {
293 let value = annotations::expect_ident(&p.inner.value)?;
294 let value = kcl_value::UnitLen::from_str(value, annotation.as_source_range())?;
295 self.default_length_units = value;
296 }
297 annotations::SETTINGS_UNIT_ANGLE => {
298 let value = annotations::expect_ident(&p.inner.value)?;
299 let value = kcl_value::UnitAngle::from_str(value, annotation.as_source_range())?;
300 self.default_angle_units = value;
301 }
302 name => {
303 return Err(KclError::Semantic(KclErrorDetails {
304 message: format!(
305 "Unexpected settings key: `{name}`; expected one of `{}`, `{}`",
306 annotations::SETTINGS_UNIT_LENGTH,
307 annotations::SETTINGS_UNIT_ANGLE
308 ),
309 source_ranges: vec![annotation.as_source_range()],
310 }))
311 }
312 }
313 }
314
315 Ok(())
316 }
317}
318
319#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq)]
321#[serde(rename_all = "camelCase")]
322pub struct IdGenerator {
323 pub(super) next_id: usize,
324 ids: Vec<uuid::Uuid>,
325}
326
327impl IdGenerator {
328 pub fn new() -> Self {
329 Self::default()
330 }
331
332 pub fn next_uuid(&mut self) -> uuid::Uuid {
333 if let Some(id) = self.ids.get(self.next_id) {
334 self.next_id += 1;
335 *id
336 } else {
337 let id = uuid::Uuid::new_v4();
338 self.ids.push(id);
339 self.next_id += 1;
340 id
341 }
342 }
343}