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;
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
94pub use engine::AsyncTasks;
95pub use engine::EngineBatchContext;
96pub use engine::EngineStats;
97pub use errors::BacktraceItem;
98pub use errors::BacktraceItemKind;
99pub use errors::CompilationIssue;
100pub use errors::CompilationIssueReport;
101pub use errors::ConnectionError;
102pub use errors::ExecError;
103pub use errors::IsRetryable;
104pub use errors::KclError;
105pub use errors::KclErrorWithOutputs;
106pub use errors::Report;
107pub use errors::ReportWithOutputs;
108pub use errors::render_compilation_issue_miette;
109pub use execution::ConstraintKind;
110pub use execution::EdgeRefactorMeta;
111pub use execution::EnvironmentRef;
112pub use execution::ExecOutcome;
113pub use execution::ExecState;
114pub use execution::ExecutionCallbacks;
115pub use execution::ExecutorContext;
116pub use execution::ExecutorSettings;
117pub use execution::KclValueView;
118pub use execution::KclVersion;
119pub use execution::LegacyAngleRefactorMeta;
120pub use execution::MetaSettings;
121pub use execution::MockConfig;
122pub use execution::OperationCallbackArgs;
123pub use execution::Point2d;
124pub use execution::RefactorMetadata;
125pub use execution::SegmentDragAnchor;
126pub use execution::SketchConstraintReport;
127pub use execution::SketchConstraintStatus;
128pub use execution::bust_cache;
129pub use execution::clear_mem_cache;
130pub use execution::pre_execute_transpile;
131pub use execution::transpile_all_old_sketches_to_new;
132pub use execution::transpile_old_sketch_to_new;
133pub use execution::transpile_old_sketch_to_new_ast;
134pub use execution::transpile_old_sketch_to_new_with_execution;
135pub use execution::typed_path::TypedPath;
136pub use fs::FileSystem;
137pub use fs::FileSystemHandle;
138pub use fs::in_memory::InMemoryFiles;
139pub use fs::new_file_system_handle;
140pub use kcl_error;
141pub use kcl_error::SourceRange;
142pub use lsp::ToLspRange;
143pub use lsp::copilot::Backend as CopilotLspBackend;
144pub use lsp::kcl::Backend as KclLspBackend;
145pub use lsp::kcl::Server as KclLspServerSubCommand;
146pub use modules::ModuleId;
147pub use parsing::ast::types::FormatOptions;
148pub use parsing::ast::types::NodePath;
149pub use parsing::ast::types::NodePathExt;
150pub use parsing::ast::types::Program as AstProgram;
151pub use parsing::ast::types::Step as NodePathStep;
152pub use project::ProjectManager;
153pub use runtime_flags::KclRuntimeFlags;
154pub use runtime_flags::RuntimeFlag;
155pub use runtime_flags::kcl_runtime_flags;
156pub use runtime_flags::set_kcl_runtime_flags;
157pub use settings::types::Configuration;
158pub use settings::types::project::ProjectConfiguration;
159#[cfg(not(target_arch = "wasm32"))]
160pub use unparser::recast_dir;
161#[cfg(not(target_arch = "wasm32"))]
162pub use unparser::walk_dir;
163
164pub mod engine_connection {
165 pub use crate::engine::engine_manager::EngineManager;
166 pub use crate::engine::engine_manager::EngineTransport;
167 pub use crate::engine::engine_manager::ResponseInformation;
168 pub use crate::engine::engine_manager::SocketHealth;
169 pub use crate::engine::engine_manager::TransportCloseError;
170}
171
172pub mod exec {
175 pub use kcl_api::NumericType;
176 pub use kcl_api::UnitAngle;
177 pub use kcl_api::UnitLength;
178 pub use kcl_api::UnitType;
179
180 pub use crate::execution::ArtifactCommand;
181 pub use crate::execution::DefaultPlanes;
182 pub use crate::execution::IdGenerator;
183 pub use crate::execution::KclObjectKind;
184 pub use crate::execution::KclValue;
185 pub use crate::execution::KclValueView;
186 pub use crate::execution::Operation;
187 pub use crate::execution::PlaneKind;
188 pub use crate::execution::Sketch;
189 pub use crate::execution::annotations::WarningLevel;
190 pub use crate::util::RetryConfig;
191 pub use crate::util::execute_with_retries;
192}
193
194#[cfg(target_arch = "wasm32")]
195pub mod wasm_engine {
196 pub use crate::engine::conn_wasm::EngineCommandManager;
197 pub use crate::engine::conn_wasm::EngineConnection;
198 pub use crate::engine::conn_wasm::ResponseContext;
199 pub use crate::fs::wasm::FileManager;
200 pub use crate::fs::wasm::FileSystemManager;
201}
202
203pub mod std_utils {
204 pub use crate::std::utils::TangentialArcInfoInput;
205 pub use crate::std::utils::get_tangential_arc_to_info;
206 pub use crate::std::utils::is_points_ccw_wasm;
207 pub use crate::std::utils::untyped_point_to_unit;
208}
209
210pub mod pretty {
211 pub use crate::fmt::format_number_literal;
212 pub use crate::fmt::format_number_value;
213 pub use crate::fmt::human_display_number;
214 pub use crate::parsing::token::NumericSuffix;
215}
216
217pub mod front {
218 pub use crate::frontend::MAX_SKETCH_CHECKPOINTS;
219 pub(crate) use crate::frontend::modify::find_defined_names;
220 pub(crate) use crate::frontend::modify::next_free_name_using_max;
221 pub use crate::frontend::sketch::ExecResult;
222 pub use crate::frontend::{
223 EditConstraintOptions,
224 EditDistanceConstraintLabelPositionOptions,
225 EditSegmentsOptions,
226 FrontendState,
227 SetProgramOutcome,
228 api::{
229 Cap, CapKind, EditSketchOutcome, Error, Expr, Face, File, FileId, LifecycleApi, NewSketchOutcome, Number,
230 Object, ObjectId, ObjectKind, Plane, ProjectId, RestoreSketchCheckpointOutcome, Result, SceneGraph,
231 SceneGraphDelta, Settings, SketchCheckpointId, SketchMutationOutcome, SourceDelta, SourceRef, Version,
232 Wall,
233 },
234 sketch::{
235 Angle, Arc, ArcCtor, ArcDirection, Circle, CircleCtor, Coincident, Constraint, ConstraintLabelPositionEdit,
236 ControlPointSpline, ControlPointSplineCtor, Distance, EqualRadius, ExistingSegmentCtor, Fixed, FixedPoint,
237 Freedom, Horizontal, Line, LineCtor, LinesEqualLength, Midpoint, NewSegmentInfo, Parallel, Perpendicular,
238 Point, Point2d, PointCtor, Segment, SegmentCtor, Sketch, SketchApi, SketchCtor, StartOrEnd, Symmetric,
239 Tangent, Vertical,
240 },
241 trim::{
243 ArcPoint, AttachToEndpoint, CoincidentData, ConstraintToMigrate, Coords2d, EndpointChanged, LineEndpoint,
244 TrimDirection, TrimItem, TrimOperation, TrimTermination, TrimTerminations, execute_trim_loop_with_context,
245 get_next_trim_spawn, get_position_coords_for_line, get_position_coords_from_arc, is_point_on_line_segment,
246 line_segment_intersection, perpendicular_distance_to_segment, project_point_onto_arc,
247 project_point_onto_segment,
248 },
249 };
250}
251
252use serde::Deserialize;
253use serde::Serialize;
254
255use crate::exec::WarningLevel;
256#[allow(unused_imports)]
257use crate::log::log;
258#[allow(unused_imports)]
259use crate::log::logln;
260
261lazy_static::lazy_static! {
262
263 pub static ref IMPORT_FILE_EXTENSIONS: Vec<String> = {
264 import_format::IMPORT_FILE_EXTENSION_FORMATS
265 .iter()
266 .map(|(extension, _)| (*extension).to_owned())
267 .collect()
268 };
269
270 pub static ref RELEVANT_FILE_EXTENSIONS: Vec<String> = {
271 let mut relevant_extensions = IMPORT_FILE_EXTENSIONS.clone();
272 relevant_extensions.push("kcl".to_string());
273 relevant_extensions.push("md".to_string());
274 relevant_extensions
275 };
276}
277
278#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
279pub struct Program {
280 #[serde(flatten)]
281 pub ast: parsing::ast::types::Node<parsing::ast::types::Program>,
282 #[serde(skip)]
286 pub original_file_contents: String,
287}
288
289#[cfg(any(test, feature = "lsp-test-util"))]
290pub use lsp::test_util::copilot_lsp_server;
291#[cfg(any(test, feature = "lsp-test-util"))]
292pub use lsp::test_util::kcl_lsp_server;
293
294impl Program {
295 pub fn parse(input: &str) -> Result<(Option<Program>, Vec<CompilationIssue>), KclError> {
296 let module_id = ModuleId::default();
297 let (ast, errs) = parsing::parse_str(input, module_id).0?;
298
299 Ok((
300 ast.map(|ast| Program {
301 ast,
302 original_file_contents: input.to_string(),
303 }),
304 errs,
305 ))
306 }
307
308 pub fn parse_no_errs(input: &str) -> Result<Program, KclError> {
309 let module_id = ModuleId::default();
310 let ast = parsing::parse_str(input, module_id).parse_errs_as_err()?;
311
312 Ok(Program {
313 ast,
314 original_file_contents: input.to_string(),
315 })
316 }
317
318 pub fn compute_digest(&mut self) -> parsing::ast::digest::Digest {
319 self.ast.compute_digest()
320 }
321
322 pub fn meta_settings(&self) -> Result<Option<crate::MetaSettings>, KclError> {
324 self.ast.meta_settings()
325 }
326
327 pub fn change_default_units(
329 &self,
330 length_units: Option<kittycad_modeling_cmds::units::UnitLength>,
331 ) -> Result<Self, KclError> {
332 Ok(Self {
333 ast: self.ast.change_default_units(length_units)?,
334 original_file_contents: self.original_file_contents.clone(),
335 })
336 }
337
338 pub fn change_kcl_version(&self, kcl_version: Option<String>) -> Result<Self, KclError> {
339 Ok(Self {
340 ast: self.ast.change_kcl_version(kcl_version)?,
341 original_file_contents: self.original_file_contents.clone(),
342 })
343 }
344
345 pub fn change_experimental_features(&self, warning_level: Option<WarningLevel>) -> Result<Self, KclError> {
346 Ok(Self {
347 ast: self.ast.change_experimental_features(warning_level)?,
348 original_file_contents: self.original_file_contents.clone(),
349 })
350 }
351
352 pub fn is_empty_or_only_settings(&self) -> bool {
353 self.ast.is_empty_or_only_settings()
354 }
355
356 pub fn lint_all(&self) -> Result<Vec<lint::Discovered>, anyhow::Error> {
357 self.ast.lint_all()
358 }
359
360 pub fn lint_all_with_options(&self, options: lint::LintOptions) -> Result<Vec<lint::Discovered>, anyhow::Error> {
361 self.ast.lint_all_with_options(options)
362 }
363
364 pub fn lint<'a>(&'a self, rule: impl lint::Rule<'a>) -> Result<Vec<lint::Discovered>, anyhow::Error> {
365 self.ast.lint(rule)
366 }
367
368 pub fn node_path_from_range(&self, cached_body_items: usize, range: SourceRange) -> Option<NodePath> {
369 let module_infos = indexmap::IndexMap::new();
370 let programs = crate::execution::ProgramLookup::new(self.ast.clone(), module_infos);
371 NodePath::from_range(&programs, cached_body_items, range)
372 }
373
374 pub fn fill_node_paths(mut self) -> Program {
380 parsing::ast::types::fill_node_paths(&mut self.ast);
381 self
382 }
383
384 pub fn recast(&self) -> String {
385 self.ast.recast_top(&Default::default(), 0)
387 }
388
389 pub fn recast_with_options(&self, options: &FormatOptions) -> String {
390 self.ast.recast_top(options, 0)
391 }
392
393 pub fn empty() -> Self {
395 Self {
396 ast: parsing::ast::types::Node::no_src(parsing::ast::types::Program::default()),
397 original_file_contents: String::new(),
398 }
399 }
400}
401
402#[inline]
403fn try_f64_to_usize(f: f64) -> Option<usize> {
404 let i = f as usize;
405 if i as f64 == f { Some(i) } else { None }
406}
407
408#[inline]
409fn try_f64_to_u32(f: f64) -> Option<u32> {
410 let i = f as u32;
411 if i as f64 == f { Some(i) } else { None }
412}
413
414#[inline]
415fn try_f64_to_u64(f: f64) -> Option<u64> {
416 let i = f as u64;
417 if i as f64 == f { Some(i) } else { None }
418}
419
420#[inline]
421fn try_f64_to_i64(f: f64) -> Option<i64> {
422 let i = f as i64;
423 if i as f64 == f { Some(i) } else { None }
424}
425
426pub fn version() -> &'static str {
428 env!("CARGO_PKG_VERSION")
429}
430
431#[cfg(test)]
432mod test {
433 use super::*;
434
435 #[test]
436 fn proprietary_file_extensions_use_real_suffixes() {
437 for extension in ["sat", "sab", "catpart", "prt", "ipt", "x_t", "x_b", "sldprt"] {
438 assert!(IMPORT_FILE_EXTENSIONS.iter().any(|candidate| candidate == extension));
439 }
440 }
441
442 #[test]
443 fn convert_int() {
444 assert_eq!(try_f64_to_usize(0.0), Some(0));
445 assert_eq!(try_f64_to_usize(42.0), Some(42));
446 assert_eq!(try_f64_to_usize(0.00000000001), None);
447 assert_eq!(try_f64_to_usize(-1.0), None);
448 assert_eq!(try_f64_to_usize(f64::NAN), None);
449 assert_eq!(try_f64_to_usize(f64::INFINITY), None);
450 assert_eq!(try_f64_to_usize((0.1 + 0.2) * 10.0), None);
451
452 assert_eq!(try_f64_to_u32(0.0), Some(0));
453 assert_eq!(try_f64_to_u32(42.0), Some(42));
454 assert_eq!(try_f64_to_u32(0.00000000001), None);
455 assert_eq!(try_f64_to_u32(-1.0), None);
456 assert_eq!(try_f64_to_u32(f64::NAN), None);
457 assert_eq!(try_f64_to_u32(f64::INFINITY), None);
458 assert_eq!(try_f64_to_u32((0.1 + 0.2) * 10.0), None);
459
460 assert_eq!(try_f64_to_u64(0.0), Some(0));
461 assert_eq!(try_f64_to_u64(42.0), Some(42));
462 assert_eq!(try_f64_to_u64(0.00000000001), None);
463 assert_eq!(try_f64_to_u64(-1.0), None);
464 assert_eq!(try_f64_to_u64(f64::NAN), None);
465 assert_eq!(try_f64_to_u64(f64::INFINITY), None);
466 assert_eq!(try_f64_to_u64((0.1 + 0.2) * 10.0), None);
467
468 assert_eq!(try_f64_to_i64(0.0), Some(0));
469 assert_eq!(try_f64_to_i64(42.0), Some(42));
470 assert_eq!(try_f64_to_i64(0.00000000001), None);
471 assert_eq!(try_f64_to_i64(-1.0), Some(-1));
472 assert_eq!(try_f64_to_i64(f64::NAN), None);
473 assert_eq!(try_f64_to_i64(f64::INFINITY), None);
474 assert_eq!(try_f64_to_i64((0.1 + 0.2) * 10.0), None);
475 }
476}