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