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