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::{types::PrimitiveType, ExecState, KclValue},
46    parsing::ast::types::Name,
47};
48
49pub type StdFn = fn(
50    &mut ExecState,
51    Args,
52) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<KclValue, KclError>> + Send + '_>>;
53
54lazy_static! {
55    static ref CORE_FNS: Vec<Box<dyn StdLibFn>> = vec![
56        Box::new(LegLen),
57        Box::new(LegAngX),
58        Box::new(LegAngY),
59        Box::new(crate::std::appearance::Appearance),
60        Box::new(crate::std::convert::Int),
61        Box::new(crate::std::extrude::Extrude),
62        Box::new(crate::std::segment::SegEnd),
63        Box::new(crate::std::segment::SegEndX),
64        Box::new(crate::std::segment::SegEndY),
65        Box::new(crate::std::segment::SegStart),
66        Box::new(crate::std::segment::SegStartX),
67        Box::new(crate::std::segment::SegStartY),
68        Box::new(crate::std::segment::LastSegX),
69        Box::new(crate::std::segment::LastSegY),
70        Box::new(crate::std::segment::SegLen),
71        Box::new(crate::std::segment::SegAng),
72        Box::new(crate::std::segment::TangentToEnd),
73        Box::new(crate::std::segment::AngleToMatchLengthX),
74        Box::new(crate::std::segment::AngleToMatchLengthY),
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::shell::Shell),
118        Box::new(crate::std::shell::Hollow),
119        Box::new(crate::std::revolve::Revolve),
120        Box::new(crate::std::sweep::Sweep),
121        Box::new(crate::std::loft::Loft),
122        Box::new(crate::std::planes::OffsetPlane),
123        Box::new(crate::std::import::Import),
124        Box::new(crate::std::math::Acos),
125        Box::new(crate::std::math::Asin),
126        Box::new(crate::std::math::Atan),
127        Box::new(crate::std::math::Atan2),
128        Box::new(crate::std::math::Pi),
129        Box::new(crate::std::math::E),
130        Box::new(crate::std::math::Tau),
131        Box::new(crate::std::math::Sqrt),
132        Box::new(crate::std::math::Abs),
133        Box::new(crate::std::math::Rem),
134        Box::new(crate::std::math::Round),
135        Box::new(crate::std::math::Floor),
136        Box::new(crate::std::math::Ceil),
137        Box::new(crate::std::math::Min),
138        Box::new(crate::std::math::Max),
139        Box::new(crate::std::math::Pow),
140        Box::new(crate::std::math::Log),
141        Box::new(crate::std::math::Log2),
142        Box::new(crate::std::math::Log10),
143        Box::new(crate::std::math::Ln),
144        Box::new(crate::std::math::ToDegrees),
145        Box::new(crate::std::math::ToRadians),
146        Box::new(crate::std::units::Mm),
147        Box::new(crate::std::units::Inch),
148        Box::new(crate::std::units::Ft),
149        Box::new(crate::std::units::M),
150        Box::new(crate::std::units::Cm),
151        Box::new(crate::std::units::Yd),
152        Box::new(crate::std::polar::Polar),
153        Box::new(crate::std::assert::Assert),
154        Box::new(crate::std::assert::AssertEqual),
155        Box::new(crate::std::assert::AssertLessThan),
156        Box::new(crate::std::assert::AssertGreaterThan),
157        Box::new(crate::std::assert::AssertLessThanOrEq),
158        Box::new(crate::std::assert::AssertGreaterThanOrEq),
159        Box::new(crate::std::transform::Scale),
160        Box::new(crate::std::transform::Translate),
161        Box::new(crate::std::transform::Rotate),
162        Box::new(crate::std::csg::Intersect),
163        Box::new(crate::std::csg::Union),
164        Box::new(crate::std::csg::Subtract),
165    ];
166}
167
168pub fn name_in_stdlib(name: &str) -> bool {
169    CORE_FNS.iter().any(|f| f.name() == name)
170}
171
172pub fn get_stdlib_fn(name: &str) -> Option<Box<dyn StdLibFn>> {
173    CORE_FNS.iter().find(|f| f.name() == name).cloned()
174}
175
176#[derive(Clone, Debug, PartialEq, Eq)]
177pub struct StdFnProps {
178    pub name: String,
179    pub deprecated: bool,
180}
181
182impl StdFnProps {
183    fn default(name: &str) -> Self {
184        Self {
185            name: name.to_owned(),
186            deprecated: false,
187        }
188    }
189}
190
191pub(crate) fn std_fn(path: &str, fn_name: &str) -> (crate::std::StdFn, StdFnProps) {
192    match (path, fn_name) {
193        ("math", "cos") => (
194            |e, a| Box::pin(crate::std::math::cos(e, a)),
195            StdFnProps::default("std::math::cos"),
196        ),
197        ("math", "sin") => (
198            |e, a| Box::pin(crate::std::math::sin(e, a)),
199            StdFnProps::default("std::math::sin"),
200        ),
201        ("math", "tan") => (
202            |e, a| Box::pin(crate::std::math::tan(e, a)),
203            StdFnProps::default("std::math::tan"),
204        ),
205        ("sketch", "circle") => (
206            |e, a| Box::pin(crate::std::shapes::circle(e, a)),
207            StdFnProps::default("std::sketch::circle"),
208        ),
209        _ => unreachable!(),
210    }
211}
212
213pub(crate) fn std_ty(path: &str, fn_name: &str) -> (PrimitiveType, StdFnProps) {
214    match (path, fn_name) {
215        ("prelude", "Sketch") => (PrimitiveType::Sketch, StdFnProps::default("std::Sketch")),
216        ("prelude", "Solid") => (PrimitiveType::Solid, StdFnProps::default("std::Solid")),
217        ("prelude", "Plane") => (PrimitiveType::Plane, StdFnProps::default("std::Plane")),
218        ("prelude", "Face") => (PrimitiveType::Face, StdFnProps::default("std::Face")),
219        ("prelude", "Helix") => (PrimitiveType::Helix, StdFnProps::default("std::Helix")),
220        _ => unreachable!(),
221    }
222}
223
224pub struct StdLib {
225    pub fns: IndexMap<String, Box<dyn StdLibFn>>,
226}
227
228impl std::fmt::Debug for StdLib {
229    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
230        f.debug_struct("StdLib").field("fns.len()", &self.fns.len()).finish()
231    }
232}
233
234impl StdLib {
235    pub fn new() -> Self {
236        let fns = CORE_FNS
237            .clone()
238            .into_iter()
239            .map(|internal_fn| (internal_fn.name(), internal_fn))
240            .collect();
241
242        Self { fns }
243    }
244
245    // Get the combined hashmaps.
246    pub fn combined(&self) -> IndexMap<String, Box<dyn StdLibFn>> {
247        self.fns.clone()
248    }
249
250    pub fn get(&self, name: &str) -> Option<Box<dyn StdLibFn>> {
251        self.fns.get(name).cloned()
252    }
253
254    pub fn get_either(&self, name: &Name) -> FunctionKind {
255        if let Some(name) = name.local_ident() {
256            if let Some(f) = self.get(name.inner) {
257                return FunctionKind::Core(f);
258            }
259        }
260
261        FunctionKind::UserDefined
262    }
263
264    pub fn contains_key(&self, key: &str) -> bool {
265        self.fns.contains_key(key)
266    }
267}
268
269impl Default for StdLib {
270    fn default() -> Self {
271        Self::new()
272    }
273}
274
275#[derive(Debug)]
276pub enum FunctionKind {
277    Core(Box<dyn StdLibFn>),
278    UserDefined,
279}
280
281/// Compute the length of the given leg.
282pub async fn leg_length(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
283    let (hypotenuse, leg, ty) = args.get_hypotenuse_leg()?;
284    let result = inner_leg_length(hypotenuse, leg);
285    Ok(KclValue::from_number_with_type(result, ty, vec![args.into()]))
286}
287
288/// Compute the length of the given leg.
289///
290/// ```no_run
291/// legLen(5, 3)
292/// ```
293#[stdlib {
294    name = "legLen",
295    tags = ["utilities"],
296}]
297fn inner_leg_length(hypotenuse: f64, leg: f64) -> f64 {
298    (hypotenuse.powi(2) - f64::min(hypotenuse.abs(), leg.abs()).powi(2)).sqrt()
299}
300
301/// Compute the angle of the given leg for x.
302pub async fn leg_angle_x(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
303    let (hypotenuse, leg, ty) = args.get_hypotenuse_leg()?;
304    let result = inner_leg_angle_x(hypotenuse, leg);
305    Ok(KclValue::from_number_with_type(result, ty, vec![args.into()]))
306}
307
308/// Compute the angle of the given leg for x.
309///
310/// ```no_run
311/// legAngX(5, 3)
312/// ```
313#[stdlib {
314    name = "legAngX",
315    tags = ["utilities"],
316}]
317fn inner_leg_angle_x(hypotenuse: f64, leg: f64) -> f64 {
318    (leg.min(hypotenuse) / hypotenuse).acos().to_degrees()
319}
320
321/// Compute the angle of the given leg for y.
322pub async fn leg_angle_y(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
323    let (hypotenuse, leg, ty) = args.get_hypotenuse_leg()?;
324    let result = inner_leg_angle_y(hypotenuse, leg);
325    Ok(KclValue::from_number_with_type(result, ty, vec![args.into()]))
326}
327
328/// Compute the angle of the given leg for y.
329///
330/// ```no_run
331/// legAngY(5, 3)
332/// ```
333#[stdlib {
334    name = "legAngY",
335    tags = ["utilities"],
336}]
337fn inner_leg_angle_y(hypotenuse: f64, leg: f64) -> f64 {
338    (leg.min(hypotenuse) / hypotenuse).asin().to_degrees()
339}
340
341/// The primitive types that can be used in a KCL file.
342#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, Display, FromStr)]
343#[serde(rename_all = "lowercase")]
344#[display(style = "lowercase")]
345pub enum Primitive {
346    /// A boolean value.
347    Bool,
348    /// A number value.
349    Number,
350    /// A string value.
351    String,
352    /// A uuid value.
353    Uuid,
354}