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