1#![recursion_limit = "1024"]
6#![allow(clippy::boxed_local)]
7
8#[allow(unused_macros)]
9macro_rules! println {
10    ($($rest:tt)*) => {
11        #[cfg(feature = "disable-println")]
12        {
13            let _ = format!($($rest)*);
14        }
15        #[cfg(not(feature = "disable-println"))]
16        std::println!($($rest)*)
17    }
18}
19
20#[allow(unused_macros)]
21macro_rules! eprintln {
22    ($($rest:tt)*) => {
23        #[cfg(feature = "disable-println")]
24        {
25            let _ = format!($($rest)*);
26        }
27        #[cfg(not(feature = "disable-println"))]
28        std::eprintln!($($rest)*)
29    }
30}
31
32#[allow(unused_macros)]
33macro_rules! print {
34    ($($rest:tt)*) => {
35        #[cfg(feature = "disable-println")]
36        {
37            let _ = format!($($rest)*);
38        }
39        #[cfg(not(feature = "disable-println"))]
40        std::print!($($rest)*)
41    }
42}
43
44#[allow(unused_macros)]
45macro_rules! eprint {
46    ($($rest:tt)*) => {
47        #[cfg(feature = "disable-println")]
48        {
49            let _ = format!($($rest)*);
50        }
51        #[cfg(not(feature = "disable-println"))]
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 fs;
65pub mod lint;
66mod log;
67mod lsp;
68mod modules;
69mod parsing;
70mod settings;
71#[cfg(test)]
72mod simulation_tests;
73mod source_range;
74pub mod std;
75#[cfg(not(target_arch = "wasm32"))]
76pub mod test_server;
77mod thread;
78mod unparser;
79pub mod walk;
80#[cfg(target_arch = "wasm32")]
81mod wasm;
82
83pub use coredump::CoreDump;
84pub use engine::{EngineManager, ExecutionKind};
85pub use errors::{CompilationError, ConnectionError, ExecError, KclError, KclErrorWithOutputs};
86pub use execution::{
87    bust_cache, clear_mem_cache, ExecOutcome, ExecState, ExecutorContext, ExecutorSettings, MetaSettings, Point2d,
88};
89pub use lsp::{
90    copilot::Backend as CopilotLspBackend,
91    kcl::{Backend as KclLspBackend, Server as KclLspServerSubCommand},
92};
93pub use modules::ModuleId;
94pub use parsing::ast::{modify::modify_ast_for_sketch, types::FormatOptions};
95pub use settings::types::{project::ProjectConfiguration, Configuration, UnitLength};
96pub use source_range::SourceRange;
97
98pub mod exec {
101    pub use crate::execution::{ArtifactCommand, DefaultPlanes, IdGenerator, KclValue, PlaneType, Sketch};
102}
103
104#[cfg(target_arch = "wasm32")]
105pub mod wasm_engine {
106    pub use crate::{
107        coredump::wasm::{CoreDumpManager, CoreDumper},
108        engine::conn_wasm::{EngineCommandManager, EngineConnection},
109        fs::wasm::FileSystemManager,
110    };
111}
112
113#[cfg(not(target_arch = "wasm32"))]
114pub mod native_engine {
115    pub use crate::engine::conn::EngineConnection;
116}
117
118pub mod std_utils {
119    pub use crate::std::utils::{get_tangential_arc_to_info, is_points_ccw_wasm, TangentialArcInfoInput};
120}
121
122pub mod pretty {
123    pub use crate::{parsing::token::NumericSuffix, unparser::format_number};
124}
125
126use serde::{Deserialize, Serialize};
127
128#[allow(unused_imports)]
129use crate::log::{log, logln};
130
131#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
132pub struct Program {
133    #[serde(flatten)]
134    pub ast: parsing::ast::types::Node<parsing::ast::types::Program>,
135}
136
137#[cfg(any(test, feature = "lsp-test-util"))]
138pub use lsp::test_util::copilot_lsp_server;
139#[cfg(any(test, feature = "lsp-test-util"))]
140pub use lsp::test_util::kcl_lsp_server;
141
142impl Program {
143    pub fn parse(input: &str) -> Result<(Option<Program>, Vec<CompilationError>), KclError> {
144        let module_id = ModuleId::default();
145        let tokens = parsing::token::lex(input, module_id)?;
146        let (ast, errs) = parsing::parse_tokens(tokens).0?;
147
148        Ok((ast.map(|ast| Program { ast }), errs))
149    }
150
151    pub fn parse_no_errs(input: &str) -> Result<Program, KclError> {
152        let module_id = ModuleId::default();
153        let tokens = parsing::token::lex(input, module_id)?;
154        let ast = parsing::parse_tokens(tokens).parse_errs_as_err()?;
155
156        Ok(Program { ast })
157    }
158
159    pub fn compute_digest(&mut self) -> parsing::ast::digest::Digest {
160        self.ast.compute_digest()
161    }
162
163    pub fn meta_settings(&self) -> Result<Option<crate::MetaSettings>, KclError> {
165        self.ast.meta_settings()
166    }
167
168    pub fn change_meta_settings(&mut self, settings: crate::MetaSettings) -> Result<Self, KclError> {
170        Ok(Self {
171            ast: self.ast.change_meta_settings(settings)?,
172        })
173    }
174
175    pub fn lint_all(&self) -> Result<Vec<lint::Discovered>, anyhow::Error> {
176        self.ast.lint_all()
177    }
178
179    pub fn lint<'a>(&'a self, rule: impl lint::Rule<'a>) -> Result<Vec<lint::Discovered>, anyhow::Error> {
180        self.ast.lint(rule)
181    }
182
183    pub fn recast(&self) -> String {
184        self.ast.recast(&Default::default(), 0)
186    }
187
188    pub fn recast_with_options(&self, options: &FormatOptions) -> String {
189        self.ast.recast(options, 0)
190    }
191}
192
193impl From<parsing::ast::types::Node<parsing::ast::types::Program>> for Program {
194    fn from(ast: parsing::ast::types::Node<parsing::ast::types::Program>) -> Program {
195        Self { ast }
196    }
197}
198
199#[inline]
200fn try_f64_to_usize(f: f64) -> Option<usize> {
201    let i = f as usize;
202    if i as f64 == f {
203        Some(i)
204    } else {
205        None
206    }
207}
208
209#[inline]
210fn try_f64_to_u32(f: f64) -> Option<u32> {
211    let i = f as u32;
212    if i as f64 == f {
213        Some(i)
214    } else {
215        None
216    }
217}
218
219#[inline]
220fn try_f64_to_u64(f: f64) -> Option<u64> {
221    let i = f as u64;
222    if i as f64 == f {
223        Some(i)
224    } else {
225        None
226    }
227}
228
229#[inline]
230fn try_f64_to_i64(f: f64) -> Option<i64> {
231    let i = f as i64;
232    if i as f64 == f {
233        Some(i)
234    } else {
235        None
236    }
237}
238
239pub fn version() -> &'static str {
241    env!("CARGO_PKG_VERSION")
242}
243
244#[cfg(test)]
245mod test {
246    use super::*;
247
248    #[test]
249    fn convert_int() {
250        assert_eq!(try_f64_to_usize(0.0), Some(0));
251        assert_eq!(try_f64_to_usize(42.0), Some(42));
252        assert_eq!(try_f64_to_usize(0.00000000001), None);
253        assert_eq!(try_f64_to_usize(-1.0), None);
254        assert_eq!(try_f64_to_usize(f64::NAN), None);
255        assert_eq!(try_f64_to_usize(f64::INFINITY), None);
256        assert_eq!(try_f64_to_usize((0.1 + 0.2) * 10.0), None);
257
258        assert_eq!(try_f64_to_u32(0.0), Some(0));
259        assert_eq!(try_f64_to_u32(42.0), Some(42));
260        assert_eq!(try_f64_to_u32(0.00000000001), None);
261        assert_eq!(try_f64_to_u32(-1.0), None);
262        assert_eq!(try_f64_to_u32(f64::NAN), None);
263        assert_eq!(try_f64_to_u32(f64::INFINITY), None);
264        assert_eq!(try_f64_to_u32((0.1 + 0.2) * 10.0), None);
265
266        assert_eq!(try_f64_to_u64(0.0), Some(0));
267        assert_eq!(try_f64_to_u64(42.0), Some(42));
268        assert_eq!(try_f64_to_u64(0.00000000001), None);
269        assert_eq!(try_f64_to_u64(-1.0), None);
270        assert_eq!(try_f64_to_u64(f64::NAN), None);
271        assert_eq!(try_f64_to_u64(f64::INFINITY), None);
272        assert_eq!(try_f64_to_u64((0.1 + 0.2) * 10.0), None);
273
274        assert_eq!(try_f64_to_i64(0.0), Some(0));
275        assert_eq!(try_f64_to_i64(42.0), Some(42));
276        assert_eq!(try_f64_to_i64(0.00000000001), None);
277        assert_eq!(try_f64_to_i64(-1.0), Some(-1));
278        assert_eq!(try_f64_to_i64(f64::NAN), None);
279        assert_eq!(try_f64_to_i64(f64::INFINITY), None);
280        assert_eq!(try_f64_to_i64((0.1 + 0.2) * 10.0), None);
281    }
282}