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