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