kcl_lib/std/
mod.rs

1//! Functions implemented for language execution.
2
3pub mod appearance;
4pub mod args;
5pub mod array;
6pub mod assert;
7pub mod axis_or_reference;
8pub mod chamfer;
9pub mod convert;
10pub mod csg;
11pub mod edge;
12pub mod extrude;
13pub mod fillet;
14pub mod helix;
15pub mod import;
16pub mod loft;
17pub mod math;
18pub mod mirror;
19pub mod patterns;
20pub mod planes;
21pub mod polar;
22pub mod revolve;
23pub mod segment;
24pub mod shapes;
25pub mod shell;
26pub mod sketch;
27pub mod sweep;
28pub mod transform;
29pub mod types;
30pub mod units;
31pub mod utils;
32
33use anyhow::Result;
34pub use args::Args;
35use indexmap::IndexMap;
36use kcl_derive_docs::stdlib;
37use lazy_static::lazy_static;
38use parse_display::{Display, FromStr};
39use schemars::JsonSchema;
40use serde::{Deserialize, Serialize};
41
42use crate::{
43    docs::StdLibFn,
44    errors::KclError,
45    execution::{ExecState, KclValue},
46};
47
48pub type StdFn = fn(
49    &mut ExecState,
50    Args,
51) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<KclValue, KclError>> + Send + '_>>;
52
53lazy_static! {
54    static ref CORE_FNS: Vec<Box<dyn StdLibFn>> = vec![
55        Box::new(LegLen),
56        Box::new(LegAngX),
57        Box::new(LegAngY),
58        Box::new(crate::std::appearance::Appearance),
59        Box::new(crate::std::convert::Int),
60        Box::new(crate::std::extrude::Extrude),
61        Box::new(crate::std::segment::SegEnd),
62        Box::new(crate::std::segment::SegEndX),
63        Box::new(crate::std::segment::SegEndY),
64        Box::new(crate::std::segment::SegStart),
65        Box::new(crate::std::segment::SegStartX),
66        Box::new(crate::std::segment::SegStartY),
67        Box::new(crate::std::segment::LastSegX),
68        Box::new(crate::std::segment::LastSegY),
69        Box::new(crate::std::segment::SegLen),
70        Box::new(crate::std::segment::SegAng),
71        Box::new(crate::std::segment::TangentToEnd),
72        Box::new(crate::std::segment::AngleToMatchLengthX),
73        Box::new(crate::std::segment::AngleToMatchLengthY),
74        Box::new(crate::std::shapes::Circle),
75        Box::new(crate::std::shapes::CircleThreePoint),
76        Box::new(crate::std::shapes::Polygon),
77        Box::new(crate::std::sketch::Line),
78        Box::new(crate::std::sketch::XLine),
79        Box::new(crate::std::sketch::YLine),
80        Box::new(crate::std::sketch::AngledLineToX),
81        Box::new(crate::std::sketch::AngledLineToY),
82        Box::new(crate::std::sketch::AngledLine),
83        Box::new(crate::std::sketch::AngledLineOfXLength),
84        Box::new(crate::std::sketch::AngledLineOfYLength),
85        Box::new(crate::std::sketch::AngledLineThatIntersects),
86        Box::new(crate::std::sketch::StartSketchOn),
87        Box::new(crate::std::sketch::StartProfileAt),
88        Box::new(crate::std::sketch::ProfileStartX),
89        Box::new(crate::std::sketch::ProfileStartY),
90        Box::new(crate::std::sketch::ProfileStart),
91        Box::new(crate::std::sketch::Close),
92        Box::new(crate::std::sketch::Arc),
93        Box::new(crate::std::sketch::ArcTo),
94        Box::new(crate::std::sketch::TangentialArc),
95        Box::new(crate::std::sketch::TangentialArcTo),
96        Box::new(crate::std::sketch::TangentialArcToRelative),
97        Box::new(crate::std::sketch::BezierCurve),
98        Box::new(crate::std::sketch::Hole),
99        Box::new(crate::std::mirror::Mirror2D),
100        Box::new(crate::std::patterns::PatternLinear2D),
101        Box::new(crate::std::patterns::PatternLinear3D),
102        Box::new(crate::std::patterns::PatternCircular2D),
103        Box::new(crate::std::patterns::PatternCircular3D),
104        Box::new(crate::std::patterns::PatternTransform),
105        Box::new(crate::std::patterns::PatternTransform2D),
106        Box::new(crate::std::array::Reduce),
107        Box::new(crate::std::array::Map),
108        Box::new(crate::std::array::Push),
109        Box::new(crate::std::array::Pop),
110        Box::new(crate::std::chamfer::Chamfer),
111        Box::new(crate::std::fillet::Fillet),
112        Box::new(crate::std::edge::GetOppositeEdge),
113        Box::new(crate::std::edge::GetNextAdjacentEdge),
114        Box::new(crate::std::edge::GetPreviousAdjacentEdge),
115        Box::new(crate::std::edge::GetCommonEdge),
116        Box::new(crate::std::helix::Helix),
117        Box::new(crate::std::helix::HelixRevolutions),
118        Box::new(crate::std::shell::Shell),
119        Box::new(crate::std::shell::Hollow),
120        Box::new(crate::std::revolve::Revolve),
121        Box::new(crate::std::sweep::Sweep),
122        Box::new(crate::std::loft::Loft),
123        Box::new(crate::std::planes::OffsetPlane),
124        Box::new(crate::std::import::Import),
125        Box::new(crate::std::math::Acos),
126        Box::new(crate::std::math::Asin),
127        Box::new(crate::std::math::Atan),
128        Box::new(crate::std::math::Atan2),
129        Box::new(crate::std::math::Pi),
130        Box::new(crate::std::math::E),
131        Box::new(crate::std::math::Tau),
132        Box::new(crate::std::math::Sqrt),
133        Box::new(crate::std::math::Abs),
134        Box::new(crate::std::math::Rem),
135        Box::new(crate::std::math::Round),
136        Box::new(crate::std::math::Floor),
137        Box::new(crate::std::math::Ceil),
138        Box::new(crate::std::math::Min),
139        Box::new(crate::std::math::Max),
140        Box::new(crate::std::math::Pow),
141        Box::new(crate::std::math::Log),
142        Box::new(crate::std::math::Log2),
143        Box::new(crate::std::math::Log10),
144        Box::new(crate::std::math::Ln),
145        Box::new(crate::std::math::ToDegrees),
146        Box::new(crate::std::math::ToRadians),
147        Box::new(crate::std::units::Mm),
148        Box::new(crate::std::units::Inch),
149        Box::new(crate::std::units::Ft),
150        Box::new(crate::std::units::M),
151        Box::new(crate::std::units::Cm),
152        Box::new(crate::std::units::Yd),
153        Box::new(crate::std::polar::Polar),
154        Box::new(crate::std::assert::Assert),
155        Box::new(crate::std::assert::AssertEqual),
156        Box::new(crate::std::assert::AssertLessThan),
157        Box::new(crate::std::assert::AssertGreaterThan),
158        Box::new(crate::std::assert::AssertLessThanOrEq),
159        Box::new(crate::std::assert::AssertGreaterThanOrEq),
160        Box::new(crate::std::transform::Scale),
161        Box::new(crate::std::transform::Translate),
162        Box::new(crate::std::transform::Rotate),
163        Box::new(crate::std::csg::Intersect),
164        Box::new(crate::std::csg::Union),
165        Box::new(crate::std::csg::Subtract),
166    ];
167}
168
169pub fn name_in_stdlib(name: &str) -> bool {
170    CORE_FNS.iter().any(|f| f.name() == name)
171}
172
173pub fn get_stdlib_fn(name: &str) -> Option<Box<dyn StdLibFn>> {
174    CORE_FNS.iter().find(|f| f.name() == name).cloned()
175}
176
177#[derive(Clone, Debug, PartialEq, Eq)]
178pub struct StdFnProps {
179    pub name: String,
180    pub deprecated: bool,
181}
182
183impl StdFnProps {
184    fn default(name: &str) -> Self {
185        Self {
186            name: name.to_owned(),
187            deprecated: false,
188        }
189    }
190}
191
192pub(crate) fn std_fn(path: &str, fn_name: &str) -> (crate::std::StdFn, StdFnProps) {
193    match (path, fn_name) {
194        ("math", "cos") => (
195            |e, a| Box::pin(crate::std::math::cos(e, a)),
196            StdFnProps::default("std::math::cos"),
197        ),
198        ("math", "sin") => (
199            |e, a| Box::pin(crate::std::math::sin(e, a)),
200            StdFnProps::default("std::math::sin"),
201        ),
202        ("math", "tan") => (
203            |e, a| Box::pin(crate::std::math::tan(e, a)),
204            StdFnProps::default("std::math::tan"),
205        ),
206        _ => unreachable!(),
207    }
208}
209
210pub(crate) fn std_ty(path: &str, fn_name: &str) -> (crate::execution::PrimitiveType, StdFnProps) {
211    match (path, fn_name) {
212        ("prelude", "Sketch") => (
213            crate::execution::PrimitiveType::Sketch,
214            StdFnProps::default("std::Sketch"),
215        ),
216        ("prelude", "Solid") => (
217            crate::execution::PrimitiveType::Solid,
218            StdFnProps::default("std::Solid"),
219        ),
220        ("prelude", "Plane") => (
221            crate::execution::PrimitiveType::Plane,
222            StdFnProps::default("std::Plane"),
223        ),
224        _ => unreachable!(),
225    }
226}
227
228pub struct StdLib {
229    pub fns: IndexMap<String, Box<dyn StdLibFn>>,
230}
231
232impl std::fmt::Debug for StdLib {
233    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
234        f.debug_struct("StdLib").field("fns.len()", &self.fns.len()).finish()
235    }
236}
237
238impl StdLib {
239    pub fn new() -> Self {
240        let fns = CORE_FNS
241            .clone()
242            .into_iter()
243            .map(|internal_fn| (internal_fn.name(), internal_fn))
244            .collect();
245
246        Self { fns }
247    }
248
249    // Get the combined hashmaps.
250    pub fn combined(&self) -> IndexMap<String, Box<dyn StdLibFn>> {
251        self.fns.clone()
252    }
253
254    pub fn get(&self, name: &str) -> Option<Box<dyn StdLibFn>> {
255        self.fns.get(name).cloned()
256    }
257
258    pub fn get_either(&self, name: &str) -> FunctionKind {
259        if let Some(f) = self.get(name) {
260            FunctionKind::Core(f)
261        } else {
262            FunctionKind::UserDefined
263        }
264    }
265
266    pub fn contains_key(&self, key: &str) -> bool {
267        self.fns.contains_key(key)
268    }
269}
270
271impl Default for StdLib {
272    fn default() -> Self {
273        Self::new()
274    }
275}
276
277#[derive(Debug)]
278pub enum FunctionKind {
279    Core(Box<dyn StdLibFn>),
280    UserDefined,
281}
282
283/// Compute the length of the given leg.
284pub async fn leg_length(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
285    let (hypotenuse, leg, ty) = args.get_hypotenuse_leg()?;
286    let result = inner_leg_length(hypotenuse, leg);
287    Ok(KclValue::from_number_with_type(result, ty, vec![args.into()]))
288}
289
290/// Compute the length of the given leg.
291///
292/// ```no_run
293/// legLen(5, 3)
294/// ```
295#[stdlib {
296    name = "legLen",
297    tags = ["utilities"],
298}]
299fn inner_leg_length(hypotenuse: f64, leg: f64) -> f64 {
300    (hypotenuse.powi(2) - f64::min(hypotenuse.abs(), leg.abs()).powi(2)).sqrt()
301}
302
303/// Compute the angle of the given leg for x.
304pub async fn leg_angle_x(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
305    let (hypotenuse, leg, ty) = args.get_hypotenuse_leg()?;
306    let result = inner_leg_angle_x(hypotenuse, leg);
307    Ok(KclValue::from_number_with_type(result, ty, vec![args.into()]))
308}
309
310/// Compute the angle of the given leg for x.
311///
312/// ```no_run
313/// legAngX(5, 3)
314/// ```
315#[stdlib {
316    name = "legAngX",
317    tags = ["utilities"],
318}]
319fn inner_leg_angle_x(hypotenuse: f64, leg: f64) -> f64 {
320    (leg.min(hypotenuse) / hypotenuse).acos().to_degrees()
321}
322
323/// Compute the angle of the given leg for y.
324pub async fn leg_angle_y(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
325    let (hypotenuse, leg, ty) = args.get_hypotenuse_leg()?;
326    let result = inner_leg_angle_y(hypotenuse, leg);
327    Ok(KclValue::from_number_with_type(result, ty, vec![args.into()]))
328}
329
330/// Compute the angle of the given leg for y.
331///
332/// ```no_run
333/// legAngY(5, 3)
334/// ```
335#[stdlib {
336    name = "legAngY",
337    tags = ["utilities"],
338}]
339fn inner_leg_angle_y(hypotenuse: f64, leg: f64) -> f64 {
340    (leg.min(hypotenuse) / hypotenuse).asin().to_degrees()
341}
342
343/// The primitive types that can be used in a KCL file.
344#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, Display, FromStr)]
345#[serde(rename_all = "lowercase")]
346#[display(style = "lowercase")]
347pub enum Primitive {
348    /// A boolean value.
349    Bool,
350    /// A number value.
351    Number,
352    /// A string value.
353    String,
354    /// A uuid value.
355    Uuid,
356}