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