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