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
59mod coredump;
60mod docs;
61mod engine;
62mod errors;
63mod execution;
64mod fmt;
65mod fs;
66pub mod lint;
67mod log;
68mod lsp;
69mod modules;
70mod parsing;
71mod settings;
72#[cfg(test)]
73mod simulation_tests;
74mod source_range;
75pub mod std;
76#[cfg(not(target_arch = "wasm32"))]
77pub mod test_server;
78mod thread;
79mod unparser;
80#[cfg(test)]
81mod variant_name;
82pub mod walk;
83#[cfg(target_arch = "wasm32")]
84mod wasm;
85
86pub use coredump::CoreDump;
87pub use engine::{AsyncTasks, EngineManager, EngineStats};
88pub use errors::{
89 BacktraceItem, CompilationError, ConnectionError, ExecError, KclError, KclErrorWithOutputs, Report,
90 ReportWithOutputs,
91};
92pub use execution::{
93 ExecOutcome, ExecState, ExecutorContext, ExecutorSettings, MetaSettings, Point2d, bust_cache, clear_mem_cache,
94 typed_path::TypedPath,
95 types::{UnitAngle, UnitLen},
96};
97pub use lsp::{
98 copilot::Backend as CopilotLspBackend,
99 kcl::{Backend as KclLspBackend, Server as KclLspServerSubCommand},
100};
101pub use modules::ModuleId;
102pub use parsing::ast::types::{FormatOptions, NodePath, Step as NodePathStep};
103pub use settings::types::{Configuration, UnitLength, project::ProjectConfiguration};
104pub use source_range::SourceRange;
105#[cfg(not(target_arch = "wasm32"))]
106pub use unparser::{recast_dir, walk_dir};
107
108pub mod exec {
111 #[cfg(feature = "artifact-graph")]
112 pub use crate::execution::{ArtifactCommand, Operation};
113 pub use crate::execution::{
114 DefaultPlanes, IdGenerator, KclValue, PlaneType, Sketch,
115 types::{NumericType, UnitAngle, UnitLen, UnitType},
116 };
117}
118
119#[cfg(target_arch = "wasm32")]
120pub mod wasm_engine {
121 pub use crate::{
122 coredump::wasm::{CoreDumpManager, CoreDumper},
123 engine::conn_wasm::{EngineCommandManager, EngineConnection, ResponseContext},
124 fs::wasm::{FileManager, FileSystemManager},
125 };
126}
127
128pub mod mock_engine {
129 pub use crate::engine::conn_mock::EngineConnection;
130}
131
132#[cfg(not(target_arch = "wasm32"))]
133pub mod native_engine {
134 pub use crate::engine::conn::EngineConnection;
135}
136
137pub mod std_utils {
138 pub use crate::std::utils::{TangentialArcInfoInput, get_tangential_arc_to_info, is_points_ccw_wasm};
139}
140
141pub mod pretty {
142 pub use crate::{
143 fmt::{format_number_literal, format_number_value, human_display_number},
144 parsing::token::NumericSuffix,
145 };
146}
147
148#[cfg(feature = "cli")]
149use clap::ValueEnum;
150use serde::{Deserialize, Serialize};
151
152#[allow(unused_imports)]
153use crate::log::{log, logln};
154
155lazy_static::lazy_static! {
156
157 pub static ref IMPORT_FILE_EXTENSIONS: Vec<String> = {
158 let mut import_file_extensions = vec!["stp".to_string(), "glb".to_string(), "fbxb".to_string()];
159 #[cfg(feature = "cli")]
160 let named_extensions = kittycad::types::FileImportFormat::value_variants()
161 .iter()
162 .map(|x| format!("{x}"))
163 .collect::<Vec<String>>();
164 #[cfg(not(feature = "cli"))]
165 let named_extensions = vec![]; import_file_extensions.extend_from_slice(&named_extensions);
168 import_file_extensions
169 };
170
171 pub static ref RELEVANT_FILE_EXTENSIONS: Vec<String> = {
172 let mut relevant_extensions = IMPORT_FILE_EXTENSIONS.clone();
173 relevant_extensions.push("kcl".to_string());
174 relevant_extensions
175 };
176}
177
178#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
179pub struct Program {
180 #[serde(flatten)]
181 pub ast: parsing::ast::types::Node<parsing::ast::types::Program>,
182 #[serde(skip)]
186 pub original_file_contents: String,
187}
188
189#[cfg(any(test, feature = "lsp-test-util"))]
190pub use lsp::test_util::copilot_lsp_server;
191#[cfg(any(test, feature = "lsp-test-util"))]
192pub use lsp::test_util::kcl_lsp_server;
193
194impl Program {
195 pub fn parse(input: &str) -> Result<(Option<Program>, Vec<CompilationError>), KclError> {
196 let module_id = ModuleId::default();
197 let (ast, errs) = parsing::parse_str(input, module_id).0?;
198
199 Ok((
200 ast.map(|ast| Program {
201 ast,
202 original_file_contents: input.to_string(),
203 }),
204 errs,
205 ))
206 }
207
208 pub fn parse_no_errs(input: &str) -> Result<Program, KclError> {
209 let module_id = ModuleId::default();
210 let ast = parsing::parse_str(input, module_id).parse_errs_as_err()?;
211
212 Ok(Program {
213 ast,
214 original_file_contents: input.to_string(),
215 })
216 }
217
218 pub fn compute_digest(&mut self) -> parsing::ast::digest::Digest {
219 self.ast.compute_digest()
220 }
221
222 pub fn meta_settings(&self) -> Result<Option<crate::MetaSettings>, KclError> {
224 self.ast.meta_settings()
225 }
226
227 pub fn change_default_units(
229 &self,
230 length_units: Option<execution::types::UnitLen>,
231 angle_units: Option<execution::types::UnitAngle>,
232 ) -> Result<Self, KclError> {
233 Ok(Self {
234 ast: self.ast.change_default_units(length_units, angle_units)?,
235 original_file_contents: self.original_file_contents.clone(),
236 })
237 }
238
239 pub fn is_empty_or_only_settings(&self) -> bool {
240 self.ast.is_empty_or_only_settings()
241 }
242
243 pub fn lint_all(&self) -> Result<Vec<lint::Discovered>, anyhow::Error> {
244 self.ast.lint_all()
245 }
246
247 pub fn lint<'a>(&'a self, rule: impl lint::Rule<'a>) -> Result<Vec<lint::Discovered>, anyhow::Error> {
248 self.ast.lint(rule)
249 }
250
251 pub fn node_path_from_range(&self, cached_body_items: usize, range: SourceRange) -> Option<NodePath> {
252 NodePath::from_range(&self.ast, cached_body_items, range)
253 }
254
255 pub fn recast(&self) -> String {
256 self.ast.recast_top(&Default::default(), 0)
258 }
259
260 pub fn recast_with_options(&self, options: &FormatOptions) -> String {
261 self.ast.recast_top(options, 0)
262 }
263
264 pub fn empty() -> Self {
266 Self {
267 ast: parsing::ast::types::Node::no_src(parsing::ast::types::Program::default()),
268 original_file_contents: String::new(),
269 }
270 }
271}
272
273#[inline]
274fn try_f64_to_usize(f: f64) -> Option<usize> {
275 let i = f as usize;
276 if i as f64 == f { Some(i) } else { None }
277}
278
279#[inline]
280fn try_f64_to_u32(f: f64) -> Option<u32> {
281 let i = f as u32;
282 if i as f64 == f { Some(i) } else { None }
283}
284
285#[inline]
286fn try_f64_to_u64(f: f64) -> Option<u64> {
287 let i = f as u64;
288 if i as f64 == f { Some(i) } else { None }
289}
290
291#[inline]
292fn try_f64_to_i64(f: f64) -> Option<i64> {
293 let i = f as i64;
294 if i as f64 == f { Some(i) } else { None }
295}
296
297pub fn version() -> &'static str {
299 env!("CARGO_PKG_VERSION")
300}
301
302#[cfg(test)]
303mod test {
304 use super::*;
305
306 #[test]
307 fn convert_int() {
308 assert_eq!(try_f64_to_usize(0.0), Some(0));
309 assert_eq!(try_f64_to_usize(42.0), Some(42));
310 assert_eq!(try_f64_to_usize(0.00000000001), None);
311 assert_eq!(try_f64_to_usize(-1.0), None);
312 assert_eq!(try_f64_to_usize(f64::NAN), None);
313 assert_eq!(try_f64_to_usize(f64::INFINITY), None);
314 assert_eq!(try_f64_to_usize((0.1 + 0.2) * 10.0), None);
315
316 assert_eq!(try_f64_to_u32(0.0), Some(0));
317 assert_eq!(try_f64_to_u32(42.0), Some(42));
318 assert_eq!(try_f64_to_u32(0.00000000001), None);
319 assert_eq!(try_f64_to_u32(-1.0), None);
320 assert_eq!(try_f64_to_u32(f64::NAN), None);
321 assert_eq!(try_f64_to_u32(f64::INFINITY), None);
322 assert_eq!(try_f64_to_u32((0.1 + 0.2) * 10.0), None);
323
324 assert_eq!(try_f64_to_u64(0.0), Some(0));
325 assert_eq!(try_f64_to_u64(42.0), Some(42));
326 assert_eq!(try_f64_to_u64(0.00000000001), None);
327 assert_eq!(try_f64_to_u64(-1.0), None);
328 assert_eq!(try_f64_to_u64(f64::NAN), None);
329 assert_eq!(try_f64_to_u64(f64::INFINITY), None);
330 assert_eq!(try_f64_to_u64((0.1 + 0.2) * 10.0), None);
331
332 assert_eq!(try_f64_to_i64(0.0), Some(0));
333 assert_eq!(try_f64_to_i64(42.0), Some(42));
334 assert_eq!(try_f64_to_i64(0.00000000001), None);
335 assert_eq!(try_f64_to_i64(-1.0), Some(-1));
336 assert_eq!(try_f64_to_i64(f64::NAN), None);
337 assert_eq!(try_f64_to_i64(f64::INFINITY), None);
338 assert_eq!(try_f64_to_i64((0.1 + 0.2) * 10.0), None);
339 }
340}