1#![recursion_limit = "1024"]
6#![allow(clippy::boxed_local)]
7
8#[allow(unused_macros)]
9macro_rules! println {
10 ($($rest:tt)*) => {
11 #[cfg(all(feature = "disable-println", not(test)))]
12 {
13 let _ = format!($($rest)*);
14 }
15 #[cfg(any(not(feature = "disable-println"), test))]
16 std::println!($($rest)*)
17 }
18}
19
20#[allow(unused_macros)]
21macro_rules! eprintln {
22 ($($rest:tt)*) => {
23 #[cfg(all(feature = "disable-println", not(test)))]
24 {
25 let _ = format!($($rest)*);
26 }
27 #[cfg(any(not(feature = "disable-println"), test))]
28 std::eprintln!($($rest)*)
29 }
30}
31
32#[allow(unused_macros)]
33macro_rules! print {
34 ($($rest:tt)*) => {
35 #[cfg(all(feature = "disable-println", not(test)))]
36 {
37 let _ = format!($($rest)*);
38 }
39 #[cfg(any(not(feature = "disable-println"), test))]
40 std::print!($($rest)*)
41 }
42}
43
44#[allow(unused_macros)]
45macro_rules! eprint {
46 ($($rest:tt)*) => {
47 #[cfg(all(feature = "disable-println", not(test)))]
48 {
49 let _ = format!($($rest)*);
50 }
51 #[cfg(any(not(feature = "disable-println"), test))]
52 std::eprint!($($rest)*)
53 }
54}
55#[cfg(feature = "dhat-heap")]
56#[global_allocator]
57static ALLOC: dhat::Alloc = dhat::Alloc;
58
59pub mod collections;
60mod docs;
61mod engine;
62mod errors;
63mod execution;
64mod fmt;
65mod frontend;
66mod fs;
67pub(crate) mod id;
68mod import_format;
69pub mod lint;
70mod log;
71mod lsp_types;
72mod modules;
73mod parsing;
74mod project;
75mod runtime_flags;
76mod settings;
77#[cfg(test)]
78mod simulation_tests;
79pub mod std;
80#[cfg(not(target_arch = "wasm32"))]
81pub mod test_server;
82mod thread;
83#[doc(hidden)]
84pub mod tooling;
85pub mod unit_conversion;
86mod unparser;
87mod util;
88#[cfg(test)]
89mod variant_name;
90pub mod walk;
91#[cfg(target_arch = "wasm32")]
92mod wasm;
93
94#[doc(hidden)]
96pub mod lsp_support {
97 pub mod docs {
98 pub mod kcl_doc {
99 pub use crate::docs::kcl_doc::ArgData;
100 pub use crate::docs::kcl_doc::DocData;
101 pub use crate::docs::kcl_doc::ModData;
102 pub use crate::docs::kcl_doc::walk_stdlib;
103 }
104 }
105
106 pub mod engine {
107 #[cfg(not(target_arch = "wasm32"))]
108 pub use crate::engine::new_zoo_client;
109 }
110
111 pub mod errors {
112 pub use crate::errors::Severity;
113 pub use crate::errors::Suggestion;
114 }
115
116 pub mod execution {
117 pub use crate::execution::ExecutorContext;
118 pub use crate::execution::MockConfig;
119
120 pub mod cache {
121 pub use crate::execution::cache::read_old_memory_var;
122 }
123
124 pub mod typed_path {
125 pub use crate::execution::typed_path::TypedPath;
126 }
127 }
128
129 pub mod fs {
130 pub use crate::fs::FileManager;
131 pub use crate::fs::FileSystemHandle;
132 pub use crate::fs::new_file_system_handle;
133
134 #[cfg(all(target_arch = "wasm32", not(test)))]
135 pub mod wasm {
136 pub use crate::fs::wasm::FileSystemManager;
137 }
138 }
139
140 pub mod parsing {
141 pub use crate::parsing::PIPE_OPERATOR;
142 pub use crate::parsing::parse_str;
143 pub use crate::parsing::parse_tokens;
144
145 pub mod ast {
146 pub mod types {
147 pub use crate::parsing::ast::types::*;
148 }
149 }
150
151 pub mod token {
152 pub use crate::parsing::token::LexerMode;
153 pub use crate::parsing::token::RESERVED_WORDS;
154 pub use crate::parsing::token::TokenStream;
155 pub use crate::parsing::token::lex;
156
157 pub mod adapter {
158 pub use crate::parsing::token::adapter::lex_with_diagnostics;
159 }
160 }
161 }
162}
163
164pub use engine::AsyncTasks;
165pub use engine::EngineBatchContext;
166pub use engine::EngineStats;
167pub use errors::BacktraceItem;
168pub use errors::BacktraceItemKind;
169pub use errors::CompilationIssue;
170pub use errors::CompilationIssueReport;
171pub use errors::ConnectionError;
172pub use errors::ExecError;
173pub use errors::IsRetryable;
174pub use errors::KclError;
175pub use errors::KclErrorWithOutputs;
176pub use errors::Report;
177pub use errors::ReportWithOutputs;
178pub use errors::render_compilation_issue_miette;
179pub use execution::ConstraintKind;
180pub use execution::EdgeRefactorMeta;
181pub use execution::EnvironmentRef;
182pub use execution::ExecOutcome;
183pub use execution::ExecState;
184pub use execution::ExecutionCallbacks;
185pub use execution::ExecutorContext;
186pub use execution::ExecutorSettings;
187pub use execution::KclValueView;
188pub use execution::KclVersion;
189pub use execution::LegacyAngleRefactorMeta;
190pub use execution::MetaSettings;
191pub use execution::MockConfig;
192pub use execution::OperationCallbackArgs;
193pub use execution::Point2d;
194pub use execution::RefactorMetadata;
195pub use execution::SegmentDragAnchor;
196pub use execution::SketchConstraintReport;
197pub use execution::SketchConstraintStatus;
198pub use execution::bust_cache;
199pub use execution::clear_mem_cache;
200pub use execution::typed_path::TypedPath;
201pub use fs::FileSystem;
202pub use fs::FileSystemHandle;
203pub use fs::in_memory::InMemoryFiles;
204pub use fs::new_file_system_handle;
205pub use kcl_error;
206pub use kcl_error::SourceRange;
207pub use lsp_types::IntoDiagnostic;
208pub use lsp_types::LspSuggestion;
209pub use lsp_types::ToLspRange;
210pub use modules::ModuleId;
211pub use parsing::ast::types::FormatOptions;
212pub use parsing::ast::types::NodePath;
213pub use parsing::ast::types::NodePathExt;
214pub use parsing::ast::types::Program as AstProgram;
215pub use parsing::ast::types::Step as NodePathStep;
216pub use project::ProjectManager;
217pub use runtime_flags::KclRuntimeFlags;
218pub use runtime_flags::RuntimeFlag;
219pub use runtime_flags::kcl_runtime_flags;
220pub use runtime_flags::set_kcl_runtime_flags;
221pub use settings::types::Configuration;
222pub use settings::types::project::ProjectConfiguration;
223#[cfg(not(target_arch = "wasm32"))]
224pub use unparser::recast_dir;
225#[cfg(not(target_arch = "wasm32"))]
226pub use unparser::walk_dir;
227
228pub mod engine_connection {
229 pub use crate::engine::engine_manager::EngineManager;
230 pub use crate::engine::engine_manager::EngineTransport;
231 pub use crate::engine::engine_manager::ResponseInformation;
232 pub use crate::engine::engine_manager::SocketHealth;
233 pub use crate::engine::engine_manager::TransportCloseError;
234}
235
236pub mod exec {
239 pub use kcl_api::NumericType;
240 pub use kcl_api::UnitAngle;
241 pub use kcl_api::UnitLength;
242 pub use kcl_api::UnitType;
243
244 pub use crate::execution::ArtifactCommand;
245 pub use crate::execution::DefaultPlanes;
246 pub use crate::execution::IdGenerator;
247 pub use crate::execution::KclObjectKind;
248 pub use crate::execution::KclValue;
249 pub use crate::execution::KclValueView;
250 pub use crate::execution::Operation;
251 pub use crate::execution::PlaneKind;
252 pub use crate::execution::Sketch;
253 pub use crate::execution::annotations::WarningLevel;
254 pub use crate::util::RetryConfig;
255 pub use crate::util::execute_with_retries;
256}
257
258#[cfg(target_arch = "wasm32")]
259pub mod wasm_engine {
260 pub use crate::engine::conn_wasm::EngineCommandManager;
261 pub use crate::engine::conn_wasm::EngineConnection;
262 pub use crate::engine::conn_wasm::ResponseContext;
263 pub use crate::fs::wasm::FileManager;
264 pub use crate::fs::wasm::FileSystemManager;
265}
266
267pub mod std_utils {
268 pub use crate::std::utils::TangentialArcInfoInput;
269 pub use crate::std::utils::get_tangential_arc_to_info;
270 pub use crate::std::utils::is_points_ccw_wasm;
271 pub use crate::std::utils::untyped_point_to_unit;
272}
273
274pub mod pretty {
275 pub use crate::fmt::format_number_literal;
276 pub use crate::fmt::format_number_value;
277 pub use crate::fmt::human_display_number;
278 pub use crate::parsing::token::NumericSuffix;
279}
280
281pub mod front {
282 pub use crate::frontend::MAX_SKETCH_CHECKPOINTS;
283 pub(crate) use crate::frontend::modify::find_defined_names;
284 pub(crate) use crate::frontend::modify::next_free_name_using_max;
285 pub use crate::frontend::sketch::ExecResult;
286 pub use crate::frontend::{
287 EditConstraintOptions,
288 EditDistanceConstraintLabelPositionOptions,
289 EditSegmentsOptions,
290 FrontendState,
291 SetProgramOutcome,
292 api::{
293 Cap, CapKind, EditSketchOutcome, Error, Expr, Face, File, FileId, LifecycleApi, NewSketchOutcome, Number,
294 Object, ObjectId, ObjectKind, Plane, ProjectId, RestoreSketchCheckpointOutcome, Result, SceneGraph,
295 SceneGraphDelta, Settings, SketchCheckpointId, SketchMutationOutcome, SourceDelta, SourceRef, Version,
296 Wall,
297 },
298 sketch::{
299 Angle, Arc, ArcCtor, ArcDirection, Circle, CircleCtor, Coincident, Constraint, ConstraintLabelPositionEdit,
300 ControlPointSpline, ControlPointSplineCtor, Distance, EqualRadius, ExistingSegmentCtor, Fixed, FixedPoint,
301 Freedom, Horizontal, Line, LineCtor, LinesEqualLength, Midpoint, NewSegmentInfo, Parallel, Perpendicular,
302 Point, Point2d, PointCtor, Segment, SegmentCtor, Sketch, SketchApi, SketchCtor, StartOrEnd, Symmetric,
303 Tangent, Vertical,
304 },
305 trim::{
307 ArcPoint, AttachToEndpoint, CoincidentData, ConstraintToMigrate, Coords2d, EndpointChanged, LineEndpoint,
308 TrimDirection, TrimItem, TrimOperation, TrimTermination, TrimTerminations, execute_trim_loop_with_context,
309 get_next_trim_spawn, get_position_coords_for_line, get_position_coords_from_arc, is_point_on_line_segment,
310 line_segment_intersection, perpendicular_distance_to_segment, project_point_onto_arc,
311 project_point_onto_segment,
312 },
313 };
314}
315
316use serde::Deserialize;
317use serde::Serialize;
318
319use crate::exec::WarningLevel;
320#[allow(unused_imports)]
321use crate::log::log;
322#[allow(unused_imports)]
323use crate::log::logln;
324
325lazy_static::lazy_static! {
326
327 pub static ref IMPORT_FILE_EXTENSIONS: Vec<String> = {
328 import_format::IMPORT_FILE_EXTENSION_FORMATS
329 .iter()
330 .map(|(extension, _)| (*extension).to_owned())
331 .collect()
332 };
333
334 pub static ref RELEVANT_FILE_EXTENSIONS: Vec<String> = {
335 let mut relevant_extensions = IMPORT_FILE_EXTENSIONS.clone();
336 relevant_extensions.push("kcl".to_string());
337 relevant_extensions.push("md".to_string());
338 relevant_extensions
339 };
340}
341
342#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
343pub struct Program {
344 #[serde(flatten)]
345 pub ast: parsing::ast::types::Node<parsing::ast::types::Program>,
346 #[serde(skip)]
350 pub original_file_contents: String,
351}
352
353impl Program {
354 pub fn parse(input: &str) -> Result<(Option<Program>, Vec<CompilationIssue>), KclError> {
355 let module_id = ModuleId::default();
356 let (ast, errs) = parsing::parse_str(input, module_id).0?;
357
358 Ok((
359 ast.map(|ast| Program {
360 ast,
361 original_file_contents: input.to_string(),
362 }),
363 errs,
364 ))
365 }
366
367 pub fn parse_no_errs(input: &str) -> Result<Program, KclError> {
368 let module_id = ModuleId::default();
369 let ast = parsing::parse_str(input, module_id).parse_errs_as_err()?;
370
371 Ok(Program {
372 ast,
373 original_file_contents: input.to_string(),
374 })
375 }
376
377 pub fn compute_digest(&mut self) -> parsing::ast::digest::Digest {
378 self.ast.compute_digest()
379 }
380
381 pub fn meta_settings(&self) -> Result<Option<crate::MetaSettings>, KclError> {
383 self.ast.meta_settings()
384 }
385
386 pub fn change_default_units(
388 &self,
389 length_units: Option<kittycad_modeling_cmds::units::UnitLength>,
390 ) -> Result<Self, KclError> {
391 Ok(Self {
392 ast: self.ast.change_default_units(length_units)?,
393 original_file_contents: self.original_file_contents.clone(),
394 })
395 }
396
397 pub fn change_kcl_version(&self, kcl_version: Option<String>) -> Result<Self, KclError> {
398 Ok(Self {
399 ast: self.ast.change_kcl_version(kcl_version)?,
400 original_file_contents: self.original_file_contents.clone(),
401 })
402 }
403
404 pub fn change_experimental_features(&self, warning_level: Option<WarningLevel>) -> Result<Self, KclError> {
405 Ok(Self {
406 ast: self.ast.change_experimental_features(warning_level)?,
407 original_file_contents: self.original_file_contents.clone(),
408 })
409 }
410
411 pub fn is_empty_or_only_settings(&self) -> bool {
412 self.ast.is_empty_or_only_settings()
413 }
414
415 pub fn lint_all(&self) -> Result<Vec<lint::Discovered>, anyhow::Error> {
416 self.ast.lint_all()
417 }
418
419 pub fn lint_all_with_options(&self, options: lint::LintOptions) -> Result<Vec<lint::Discovered>, anyhow::Error> {
420 self.ast.lint_all_with_options(options)
421 }
422
423 pub fn lint<'a>(&'a self, rule: impl lint::Rule<'a>) -> Result<Vec<lint::Discovered>, anyhow::Error> {
424 self.ast.lint(rule)
425 }
426
427 pub fn node_path_from_range(&self, cached_body_items: usize, range: SourceRange) -> Option<NodePath> {
428 let module_infos = indexmap::IndexMap::new();
429 let programs = crate::execution::ProgramLookup::new(self.ast.clone(), module_infos);
430 NodePath::from_range(&programs, cached_body_items, range)
431 }
432
433 pub fn fill_node_paths(mut self) -> Program {
439 parsing::ast::types::fill_node_paths(&mut self.ast);
440 self
441 }
442
443 pub fn recast(&self) -> String {
444 self.ast.recast_top(&Default::default(), 0)
446 }
447
448 pub fn recast_with_options(&self, options: &FormatOptions) -> String {
449 self.ast.recast_top(options, 0)
450 }
451
452 pub fn empty() -> Self {
454 Self {
455 ast: parsing::ast::types::Node::no_src(parsing::ast::types::Program::default()),
456 original_file_contents: String::new(),
457 }
458 }
459}
460
461#[inline]
462fn try_f64_to_usize(f: f64) -> Option<usize> {
463 let i = f as usize;
464 if i as f64 == f { Some(i) } else { None }
465}
466
467#[inline]
468fn try_f64_to_u32(f: f64) -> Option<u32> {
469 let i = f as u32;
470 if i as f64 == f { Some(i) } else { None }
471}
472
473#[inline]
474fn try_f64_to_u64(f: f64) -> Option<u64> {
475 let i = f as u64;
476 if i as f64 == f { Some(i) } else { None }
477}
478
479#[inline]
480fn try_f64_to_i64(f: f64) -> Option<i64> {
481 let i = f as i64;
482 if i as f64 == f { Some(i) } else { None }
483}
484
485pub fn version() -> &'static str {
487 env!("CARGO_PKG_VERSION")
488}
489
490#[cfg(test)]
491mod test {
492 use super::*;
493
494 #[test]
495 fn proprietary_file_extensions_use_real_suffixes() {
496 for extension in ["sat", "sab", "catpart", "prt", "ipt", "x_t", "x_b", "sldprt"] {
497 assert!(IMPORT_FILE_EXTENSIONS.iter().any(|candidate| candidate == extension));
498 }
499 }
500
501 #[test]
502 fn convert_int() {
503 assert_eq!(try_f64_to_usize(0.0), Some(0));
504 assert_eq!(try_f64_to_usize(42.0), Some(42));
505 assert_eq!(try_f64_to_usize(0.00000000001), None);
506 assert_eq!(try_f64_to_usize(-1.0), None);
507 assert_eq!(try_f64_to_usize(f64::NAN), None);
508 assert_eq!(try_f64_to_usize(f64::INFINITY), None);
509 assert_eq!(try_f64_to_usize((0.1 + 0.2) * 10.0), None);
510
511 assert_eq!(try_f64_to_u32(0.0), Some(0));
512 assert_eq!(try_f64_to_u32(42.0), Some(42));
513 assert_eq!(try_f64_to_u32(0.00000000001), None);
514 assert_eq!(try_f64_to_u32(-1.0), None);
515 assert_eq!(try_f64_to_u32(f64::NAN), None);
516 assert_eq!(try_f64_to_u32(f64::INFINITY), None);
517 assert_eq!(try_f64_to_u32((0.1 + 0.2) * 10.0), None);
518
519 assert_eq!(try_f64_to_u64(0.0), Some(0));
520 assert_eq!(try_f64_to_u64(42.0), Some(42));
521 assert_eq!(try_f64_to_u64(0.00000000001), None);
522 assert_eq!(try_f64_to_u64(-1.0), None);
523 assert_eq!(try_f64_to_u64(f64::NAN), None);
524 assert_eq!(try_f64_to_u64(f64::INFINITY), None);
525 assert_eq!(try_f64_to_u64((0.1 + 0.2) * 10.0), None);
526
527 assert_eq!(try_f64_to_i64(0.0), Some(0));
528 assert_eq!(try_f64_to_i64(42.0), Some(42));
529 assert_eq!(try_f64_to_i64(0.00000000001), None);
530 assert_eq!(try_f64_to_i64(-1.0), Some(-1));
531 assert_eq!(try_f64_to_i64(f64::NAN), None);
532 assert_eq!(try_f64_to_i64(f64::INFINITY), None);
533 assert_eq!(try_f64_to_i64((0.1 + 0.2) * 10.0), None);
534 }
535}