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