Skip to main content

kcl_lib/std/
args.rs

1use std::num::NonZeroU32;
2
3use anyhow::Result;
4use kcl_api::UnitAngle;
5use kcl_api::UnitLength;
6use kcmc::shared::BodyType;
7use kittycad_modeling_cmds as kcmc;
8use serde::Serialize;
9use uuid::Uuid;
10
11use super::fillet::EdgeReference;
12use crate::CompilationIssue;
13use crate::MetaSettings;
14use crate::ModuleId;
15use crate::SourceRange;
16use crate::errors::KclError;
17use crate::errors::KclErrorDetails;
18use crate::execution::BoundedEdge;
19use crate::execution::ExecState;
20use crate::execution::Extrudable;
21use crate::execution::ExtrudeSurface;
22use crate::execution::Face;
23use crate::execution::Geometry;
24use crate::execution::Helix;
25use crate::execution::KclObjectFields;
26use crate::execution::KclValue;
27use crate::execution::Metadata;
28use crate::execution::Plane;
29use crate::execution::PlaneInfo;
30use crate::execution::Segment;
31use crate::execution::Sketch;
32use crate::execution::SketchSurface;
33use crate::execution::Solid;
34use crate::execution::TagIdentifier;
35use crate::execution::annotations;
36pub use crate::execution::fn_call::Args;
37use crate::execution::kcl_value::FunctionSource;
38use crate::execution::types::NumericSuffixTypeConvertError;
39use crate::execution::types::NumericType;
40use crate::execution::types::NumericTypeExt;
41use crate::execution::types::PrimitiveType;
42use crate::execution::types::RuntimeType;
43use crate::execution::types::UnitType;
44use crate::front::Number;
45use crate::parsing::ast::types::TagNode;
46use crate::std::CircularDirection;
47use crate::std::edge::check_tag_not_ambiguous;
48use crate::std::shapes::PolygonType;
49use crate::std::shapes::SketchOrSurface;
50use crate::std::sketch::FaceTag;
51use crate::std::sweep::SweepPath;
52
53const ERROR_STRING_SKETCH_TO_SOLID_HELPER: &str =
54    "You can convert a sketch (2D) into a Solid (3D) by calling a function like `extrude` or `revolve`";
55
56#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
57#[ts(export)]
58#[serde(rename_all = "camelCase")]
59pub struct TyF64 {
60    pub n: f64,
61    pub ty: NumericType,
62}
63
64impl TyF64 {
65    pub const fn new(n: f64, ty: NumericType) -> Self {
66        Self { n, ty }
67    }
68
69    pub fn from_number(n: Number, settings: &MetaSettings) -> Self {
70        Self {
71            n: n.value,
72            ty: NumericType::from_parsed(n.units, settings),
73        }
74    }
75
76    pub fn to_mm(&self) -> f64 {
77        self.to_length_units(UnitLength::Millimeters)
78    }
79
80    pub fn to_length_units(&self, units: UnitLength) -> f64 {
81        let len = match &self.ty {
82            NumericType::Default { len, .. } => *len,
83            NumericType::Known(UnitType::Length(len)) => *len,
84            t => unreachable!("expected length, found {t:?}"),
85        };
86
87        crate::execution::types::adjust_length(len, self.n, units).0
88    }
89
90    pub fn to_degrees(&self, exec_state: &mut ExecState, source_range: SourceRange) -> f64 {
91        let angle = match self.ty {
92            NumericType::Default { angle, .. } => {
93                if self.n != 0.0 {
94                    exec_state.warn(
95                        CompilationIssue::err(source_range, "Prefer to use explicit units for angles"),
96                        annotations::WARN_ANGLE_UNITS,
97                    );
98                }
99                angle
100            }
101            NumericType::Known(UnitType::Angle(angle)) => angle,
102            _ => unreachable!(),
103        };
104
105        crate::execution::types::adjust_angle(angle, self.n, UnitAngle::Degrees).0
106    }
107
108    pub fn to_radians(&self, exec_state: &mut ExecState, source_range: SourceRange) -> f64 {
109        let angle = match self.ty {
110            NumericType::Default { angle, .. } => {
111                if self.n != 0.0 {
112                    exec_state.warn(
113                        CompilationIssue::err(source_range, "Prefer to use explicit units for angles"),
114                        annotations::WARN_ANGLE_UNITS,
115                    );
116                }
117                angle
118            }
119            NumericType::Known(UnitType::Angle(angle)) => angle,
120            _ => unreachable!(),
121        };
122
123        crate::execution::types::adjust_angle(angle, self.n, UnitAngle::Radians).0
124    }
125    pub fn count(n: f64) -> Self {
126        Self {
127            n,
128            ty: NumericType::count(),
129        }
130    }
131
132    pub fn map_value(mut self, n: f64) -> Self {
133        self.n = n;
134        self
135    }
136
137    // This can't be a TryFrom impl because `Point2d` is defined in another
138    // crate.
139    pub fn to_point2d(value: &[TyF64; 2]) -> Result<crate::front::Point2d<Number>, NumericSuffixTypeConvertError> {
140        Ok(crate::front::Point2d {
141            x: Number {
142                value: value[0].n,
143                units: value[0].ty.try_into()?,
144            },
145            y: Number {
146                value: value[1].n,
147                units: value[1].ty.try_into()?,
148            },
149        })
150    }
151}
152
153impl Args {
154    pub(crate) fn get_kw_arg_opt<T>(
155        &self,
156        label: &str,
157        ty: &RuntimeType,
158        exec_state: &mut ExecState,
159    ) -> Result<Option<T>, KclError>
160    where
161        T: for<'a> FromKclValue<'a>,
162    {
163        match self.labeled.get(label) {
164            None => return Ok(None),
165            Some(a) => {
166                if let KclValue::KclNone { .. } = &a.value {
167                    return Ok(None);
168                }
169            }
170        }
171
172        self.get_kw_arg(label, ty, exec_state).map(Some)
173    }
174
175    pub(crate) fn get_kw_arg<T>(&self, label: &str, ty: &RuntimeType, exec_state: &mut ExecState) -> Result<T, KclError>
176    where
177        T: for<'a> FromKclValue<'a>,
178    {
179        let Some(arg) = self.labeled.get(label) else {
180            return Err(KclError::new_semantic(KclErrorDetails::new(
181                if let Some(ref fname) = self.fn_name {
182                    format!("The `{fname}` function requires a keyword argument `{label}`")
183                } else {
184                    format!("This function requires a keyword argument `{label}`")
185                },
186                vec![self.source_range],
187            )));
188        };
189
190        let arg = arg.value.coerce(ty, true, exec_state).map_err(|_| {
191            let actual_type = arg.value.principal_type();
192            let actual_type_name = actual_type
193                .as_ref()
194                .map(|t| t.to_string())
195                .unwrap_or_else(|| arg.value.human_friendly_type());
196            let msg_base = if let Some(ref fname) = self.fn_name {
197                format!("The `{fname}` function expected its `{label}` argument to be {} but it's actually of type {actual_type_name}", ty.human_friendly_type())
198            } else {
199                format!("This function expected its `{label}` argument to be {} but it's actually of type {actual_type_name}", ty.human_friendly_type())
200            };
201            let suggestion = match (ty, actual_type) {
202                (RuntimeType::Primitive(PrimitiveType::Solid), Some(RuntimeType::Primitive(PrimitiveType::Sketch))) => {
203                    Some(ERROR_STRING_SKETCH_TO_SOLID_HELPER)
204                }
205                (RuntimeType::Array(t, _), Some(RuntimeType::Primitive(PrimitiveType::Sketch)))
206                    if **t == RuntimeType::Primitive(PrimitiveType::Solid) =>
207                {
208                    Some(ERROR_STRING_SKETCH_TO_SOLID_HELPER)
209                }
210                _ => None,
211            };
212            let mut message = match suggestion {
213                None => msg_base,
214                Some(sugg) => format!("{msg_base}. {sugg}"),
215            };
216            if message.contains("one or more Solids or ImportedGeometry but it's actually of type Sketch") {
217                message = format!("{message}. {ERROR_STRING_SKETCH_TO_SOLID_HELPER}");
218            }
219            KclError::new_semantic(KclErrorDetails::new(message, arg.source_ranges()))
220        })?;
221
222        T::from_kcl_val(&arg).ok_or_else(|| {
223            KclError::new_internal(KclErrorDetails::new(
224                format!("Mismatch between type coercion and value extraction (this isn't your fault).\nTo assist in bug-reporting, expected type: {ty:?}; actual value: {arg:?}"),
225                vec![self.source_range],
226           ))
227        })
228    }
229
230    /// Get a labelled keyword arg, check it's an array, and return all items in the array
231    /// plus their source range.
232    pub(crate) fn kw_arg_edge_array_and_source(
233        &self,
234        label: &str,
235    ) -> Result<Vec<(EdgeReference, SourceRange)>, KclError> {
236        let Some(arg) = self.labeled.get(label) else {
237            let err = KclError::new_semantic(KclErrorDetails::new(
238                if let Some(ref fname) = self.fn_name {
239                    format!("The `{fname}` function requires a keyword argument '{label}'")
240                } else {
241                    format!("This function requires a keyword argument '{label}'")
242                },
243                vec![self.source_range],
244            ));
245            return Err(err);
246        };
247        arg.value
248            .clone()
249            .into_array()
250            .iter()
251            .map(|item| {
252                let source = SourceRange::from(item);
253                let val = FromKclValue::from_kcl_val(item).ok_or_else(|| {
254                    KclError::new_semantic(KclErrorDetails::new(
255                        format!("Expected an Edge but found {}", arg.value.human_friendly_type()),
256                        arg.source_ranges(),
257                    ))
258                })?;
259                Ok((val, source))
260            })
261            .collect::<Result<Vec<_>, _>>()
262    }
263
264    pub(crate) fn kw_arg_edge_array_and_source_opt(
265        &self,
266        label: &str,
267    ) -> Result<Option<Vec<(EdgeReference, SourceRange)>>, KclError> {
268        if !self.labeled.contains_key(label) {
269            return Ok(None);
270        }
271
272        self.kw_arg_edge_array_and_source(label).map(Some)
273    }
274
275    pub(crate) fn get_unlabeled_kw_arg_array_and_type(
276        &self,
277        label: &str,
278        exec_state: &mut ExecState,
279    ) -> Result<(Vec<KclValue>, RuntimeType), KclError> {
280        let value = self.get_unlabeled_kw_arg(label, &RuntimeType::any_array(), exec_state)?;
281        Ok(match value {
282            KclValue::HomArray { value, ty } => (value, ty),
283            KclValue::Tuple { value, .. } => (value, RuntimeType::any()),
284            val => (vec![val], RuntimeType::any()),
285        })
286    }
287
288    /// Get the unlabeled keyword argument. If not set, returns Err. If it
289    /// can't be converted to the given type, returns Err.
290    pub(crate) fn get_unlabeled_kw_arg<T>(
291        &self,
292        label: &str,
293        ty: &RuntimeType,
294        exec_state: &mut ExecState,
295    ) -> Result<T, KclError>
296    where
297        T: for<'a> FromKclValue<'a>,
298    {
299        let arg = self
300            .unlabeled_kw_arg_unconverted()
301            .ok_or(KclError::new_semantic(KclErrorDetails::new(
302                if let Some(ref fname) = self.fn_name {
303                    format!(
304                        "The `{fname}` function requires a value for the special unlabeled first parameter, '{label}'"
305                    )
306                } else {
307                    format!("This function requires a value for the special unlabeled first parameter, '{label}'")
308                },
309                vec![self.source_range],
310            )))?;
311
312        let arg = arg.value.coerce(ty, true, exec_state).map_err(|_| {
313            let actual_type = arg.value.principal_type();
314            let actual_type_name = actual_type
315                .as_ref()
316                .map(|t| t.to_string())
317                .unwrap_or_else(|| arg.value.human_friendly_type());
318            let msg_base = if let Some(ref fname) = self.fn_name {
319                format!(
320                    "The `{fname}` function expected the input argument to be {} but it's actually of type {actual_type_name}",
321                    ty.human_friendly_type(),
322                )
323            } else {
324                format!(
325                    "This function expected the input argument to be {} but it's actually of type {actual_type_name}",
326                    ty.human_friendly_type(),
327                )
328            };
329            let suggestion = match (ty, actual_type) {
330                (RuntimeType::Primitive(PrimitiveType::Solid), Some(RuntimeType::Primitive(PrimitiveType::Sketch))) => {
331                    Some(ERROR_STRING_SKETCH_TO_SOLID_HELPER)
332                }
333                (RuntimeType::Array(ty, _), Some(RuntimeType::Primitive(PrimitiveType::Sketch)))
334                    if **ty == RuntimeType::Primitive(PrimitiveType::Solid) =>
335                {
336                    Some(ERROR_STRING_SKETCH_TO_SOLID_HELPER)
337                }
338                _ => None,
339            };
340            let mut message = match suggestion {
341                None => msg_base,
342                Some(sugg) => format!("{msg_base}. {sugg}"),
343            };
344
345            if message.contains("one or more Solids or ImportedGeometry but it's actually of type Sketch") {
346                message = format!("{message}. {ERROR_STRING_SKETCH_TO_SOLID_HELPER}");
347            }
348            KclError::new_semantic(KclErrorDetails::new(message, arg.source_ranges()))
349        })?;
350
351        T::from_kcl_val(&arg).ok_or_else(|| {
352            KclError::new_internal(KclErrorDetails::new(
353                format!("Mismatch between type coercion and value extraction (this isn't your fault).\nTo assist in bug-reporting, expected type: {ty:?}; actual value: {arg:?}"),
354                vec![self.source_range],
355           ))
356        })
357    }
358
359    // TODO: Move this to the modeling module.
360    fn get_tag_info_from_memory(
361        &self,
362        exec_state: &mut ExecState,
363        tag: &TagIdentifier,
364    ) -> Result<crate::execution::TagEngineInfo, KclError> {
365        match exec_state.stack().get_from_call_stack(&tag.value, self.source_range)? {
366            (epoch, KclValue::TagIdentifier(t)) => {
367                let info = t.get_info(epoch).ok_or_else(|| {
368                    KclError::new_type(KclErrorDetails::new(
369                        format!("Tag `{}` does not have engine info", tag.value),
370                        vec![self.source_range],
371                    ))
372                })?;
373                Ok(info.clone())
374            }
375            _ => Err(KclError::new_internal(KclErrorDetails::new(
376                format!("Tag `{}` is bound to an unexpected type", tag.value),
377                vec![self.source_range],
378            ))),
379        }
380    }
381
382    // TODO: Move this to the modeling module.
383    pub(crate) fn get_tag_engine_info(
384        &self,
385        exec_state: &mut ExecState,
386        tag: &TagIdentifier,
387    ) -> Result<crate::execution::TagEngineInfo, KclError> {
388        if let Some(info) = tag.get_cur_info() {
389            return Ok(info.clone());
390        }
391
392        self.get_tag_info_from_memory(exec_state, tag)
393    }
394
395    // TODO: Move this to the modeling module.
396    fn get_tag_engine_info_check_surface(
397        &self,
398        exec_state: &mut ExecState,
399        tag: &TagIdentifier,
400    ) -> Result<crate::execution::TagEngineInfo, KclError> {
401        let info = tag.get_cur_info();
402        if let Some(info) = info
403            && info.surface.is_some()
404        {
405            return Ok(info.clone());
406        }
407
408        self.get_tag_info_from_memory(exec_state, tag).map_err(|err| {
409            if err.is_undefined_value() {
410                // Looking the tag up in memory didn't find it. Provide a more
411                // helpful message.
412                self.tag_requires_face_error(tag, info)
413            } else {
414                err
415            }
416        })
417    }
418
419    fn tag_requires_face_error(&self, tag: &TagIdentifier, info: Option<&crate::execution::TagEngineInfo>) -> KclError {
420        let what = if let Some(info) = info {
421            if info.path.is_some() {
422                match &info.geometry {
423                    Geometry::Sketch(_) => "a sketch edge",
424                    Geometry::Solid(_) => "a solid edge",
425                }
426            } else {
427                match &info.geometry {
428                    Geometry::Sketch(_) => "sketch geometry",
429                    Geometry::Solid(_) => "solid geometry",
430                }
431            }
432        } else {
433            "non-face geometry"
434        };
435
436        KclError::new_type(KclErrorDetails::new(
437            format!(
438                "Tag `{}` refers to {what}, but this operation requires a face tag",
439                tag.value
440            ),
441            vec![self.source_range],
442        ))
443    }
444
445    pub(crate) fn make_kcl_val_from_point(&self, p: [f64; 2], ty: NumericType) -> Result<KclValue, KclError> {
446        let meta = Metadata {
447            source_range: self.source_range,
448        };
449        let x = KclValue::Number {
450            value: p[0],
451            meta: vec![meta],
452            ty,
453        };
454        let y = KclValue::Number {
455            value: p[1],
456            meta: vec![meta],
457            ty,
458        };
459        let ty = RuntimeType::Primitive(PrimitiveType::Number(ty));
460
461        Ok(KclValue::HomArray { value: vec![x, y], ty })
462    }
463
464    pub(super) fn make_user_val_from_f64_with_type(&self, f: TyF64) -> KclValue {
465        KclValue::from_number_with_type(
466            f.n,
467            f.ty,
468            vec![Metadata {
469                source_range: self.source_range,
470            }],
471        )
472    }
473
474    // TODO: Move this to the modeling module.
475    pub(crate) async fn get_adjacent_face_to_tag(
476        &self,
477        exec_state: &mut ExecState,
478        tag: &TagIdentifier,
479        must_be_planar: bool,
480    ) -> Result<uuid::Uuid, KclError> {
481        if tag.value.is_empty() {
482            return Err(KclError::new_type(KclErrorDetails::new(
483                "Expected a non-empty tag for the face".to_string(),
484                vec![self.source_range],
485            )));
486        }
487
488        // Check for ambiguous region-mapped tags (1:N).
489        check_tag_not_ambiguous(tag, self)?;
490
491        let engine_info = self.get_tag_engine_info_check_surface(exec_state, tag)?;
492
493        let surface = engine_info
494            .surface
495            .as_ref()
496            .ok_or_else(|| self.tag_requires_face_error(tag, Some(&engine_info)))?;
497
498        if let Some(face_from_surface) = match surface {
499            ExtrudeSurface::ExtrudePlane(extrude_plane) => {
500                if let Some(plane_tag) = &extrude_plane.tag {
501                    if plane_tag.name == tag.value {
502                        Some(Ok(extrude_plane.face_id))
503                    } else {
504                        None
505                    }
506                } else {
507                    None
508                }
509            }
510            // The must be planar check must be called before the arc check.
511            ExtrudeSurface::ExtrudeArc(_) if must_be_planar => Some(Err(KclError::new_type(KclErrorDetails::new(
512                format!("Tag `{}` is a non-planar surface", tag.value),
513                vec![self.source_range],
514            )))),
515            ExtrudeSurface::ExtrudeArc(extrude_arc) => {
516                if let Some(arc_tag) = &extrude_arc.tag {
517                    if arc_tag.name == tag.value {
518                        Some(Ok(extrude_arc.face_id))
519                    } else {
520                        None
521                    }
522                } else {
523                    None
524                }
525            }
526            ExtrudeSurface::Chamfer(chamfer) => {
527                if let Some(chamfer_tag) = &chamfer.tag {
528                    if chamfer_tag.name == tag.value {
529                        Some(Ok(chamfer.face_id))
530                    } else {
531                        None
532                    }
533                } else {
534                    None
535                }
536            }
537            // The must be planar check must be called before the fillet check.
538            ExtrudeSurface::Fillet(_) if must_be_planar => Some(Err(KclError::new_type(KclErrorDetails::new(
539                format!("Tag `{}` is a non-planar surface", tag.value),
540                vec![self.source_range],
541            )))),
542            ExtrudeSurface::Fillet(fillet) => {
543                if let Some(fillet_tag) = &fillet.tag {
544                    if fillet_tag.name == tag.value {
545                        Some(Ok(fillet.face_id))
546                    } else {
547                        None
548                    }
549                } else {
550                    None
551                }
552            }
553        } {
554            return face_from_surface;
555        }
556
557        // If we still haven't found the face, return an error.
558        Err(KclError::new_type(KclErrorDetails::new(
559            format!("Expected a face with the tag `{}`", tag.value),
560            vec![self.source_range],
561        )))
562    }
563}
564
565/// Types which impl this trait can be extracted from a `KclValue`.
566pub trait FromKclValue<'a>: Sized {
567    /// Try to convert a KclValue into this type.
568    fn from_kcl_val(arg: &'a KclValue) -> Option<Self>;
569}
570
571impl<'a> FromKclValue<'a> for TagNode {
572    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
573        arg.get_tag_declarator().ok()
574    }
575}
576
577impl<'a> FromKclValue<'a> for TagIdentifier {
578    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
579        arg.get_tag_identifier().ok()
580    }
581}
582
583impl<'a> FromKclValue<'a> for Vec<TagIdentifier> {
584    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
585        let tags = arg
586            .clone()
587            .into_array()
588            .iter()
589            .map(|v| v.get_tag_identifier().unwrap())
590            .collect();
591        Some(tags)
592    }
593}
594
595impl<'a> FromKclValue<'a> for Vec<KclValue> {
596    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
597        Some(arg.clone().into_array())
598    }
599}
600
601impl<'a> FromKclValue<'a> for Vec<Extrudable> {
602    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
603        let items = arg
604            .clone()
605            .into_array()
606            .iter()
607            .map(Extrudable::from_kcl_val)
608            .collect::<Option<Vec<_>>>()?;
609        Some(items)
610    }
611}
612
613impl<'a> FromKclValue<'a> for KclValue {
614    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
615        Some(arg.clone())
616    }
617}
618
619macro_rules! let_field_of {
620    // Optional field
621    ($obj:ident, $field:ident?) => {
622        let $field = $obj.get(stringify!($field)).and_then(FromKclValue::from_kcl_val);
623    };
624    // Optional field but with a different string used as the key
625    ($obj:ident, $field:ident? $key:literal) => {
626        let $field = $obj.get($key).and_then(FromKclValue::from_kcl_val);
627    };
628    // Mandatory field, but with a different string used as the key.
629    ($obj:ident, $field:ident $key:literal) => {
630        let $field = $obj.get($key).and_then(FromKclValue::from_kcl_val)?;
631    };
632    // Mandatory field, optionally with a type annotation
633    ($obj:ident, $field:ident $(, $annotation:ty)?) => {
634        let $field $(: $annotation)? = $obj.get(stringify!($field)).and_then(FromKclValue::from_kcl_val)?;
635    };
636}
637
638impl<'a> FromKclValue<'a> for crate::execution::Plane {
639    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
640        arg.as_plane().cloned()
641    }
642}
643
644impl<'a> FromKclValue<'a> for crate::execution::PlaneKind {
645    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
646        let plane_type = match arg.as_str()? {
647            "XY" | "xy" => Self::XY,
648            "XZ" | "xz" => Self::XZ,
649            "YZ" | "yz" => Self::YZ,
650            "Custom" => Self::Custom,
651            _ => return None,
652        };
653        Some(plane_type)
654    }
655}
656
657impl<'a> FromKclValue<'a> for BodyType {
658    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
659        let body_type = match arg.as_str()? {
660            "solid" => Self::Solid,
661            "surface" => Self::Surface,
662            _ => return None,
663        };
664        Some(body_type)
665    }
666}
667
668impl<'a> FromKclValue<'a> for CircularDirection {
669    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
670        let dir = match arg.as_str()? {
671            "ccw" => Self::Counterclockwise,
672            "cw" => Self::Clockwise,
673            _ => return None,
674        };
675        Some(dir)
676    }
677}
678
679impl<'a> FromKclValue<'a> for kittycad_modeling_cmds::units::UnitLength {
680    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
681        let s = arg.as_str()?;
682        s.parse().ok()
683    }
684}
685
686impl<'a> FromKclValue<'a> for kittycad_modeling_cmds::coord::System {
687    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
688        let obj = arg.as_object()?;
689        let_field_of!(obj, forward);
690        let_field_of!(obj, up);
691        Some(Self { forward, up })
692    }
693}
694
695impl<'a> FromKclValue<'a> for kittycad_modeling_cmds::coord::AxisDirectionPair {
696    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
697        let obj = arg.as_object()?;
698        let_field_of!(obj, axis);
699        let_field_of!(obj, direction);
700        Some(Self { axis, direction })
701    }
702}
703
704impl<'a> FromKclValue<'a> for kittycad_modeling_cmds::coord::Axis {
705    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
706        let s = arg.as_str()?;
707        match s {
708            "y" => Some(Self::Y),
709            "z" => Some(Self::Z),
710            _ => None,
711        }
712    }
713}
714
715impl<'a> FromKclValue<'a> for PolygonType {
716    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
717        let s = arg.as_str()?;
718        match s {
719            "inscribed" => Some(Self::Inscribed),
720            _ => Some(Self::Circumscribed),
721        }
722    }
723}
724
725impl<'a> FromKclValue<'a> for kittycad_modeling_cmds::coord::Direction {
726    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
727        let s = arg.as_str()?;
728        match s {
729            "positive" => Some(Self::Positive),
730            "negative" => Some(Self::Negative),
731            _ => None,
732        }
733    }
734}
735
736impl<'a> FromKclValue<'a> for crate::execution::Geometry {
737    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
738        match arg {
739            KclValue::Sketch { value } => Some(Self::Sketch(*value.to_owned())),
740            KclValue::Solid { value } => Some(Self::Solid(*value.to_owned())),
741            _ => None,
742        }
743    }
744}
745
746impl<'a> FromKclValue<'a> for crate::execution::GeometryWithImportedGeometry {
747    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
748        match arg {
749            KclValue::Sketch { value } => Some(Self::Sketch(*value.to_owned())),
750            KclValue::Solid { value } => Some(Self::Solid(*value.to_owned())),
751            KclValue::ImportedGeometry(value) => Some(Self::ImportedGeometry(Box::new(value.clone()))),
752            _ => None,
753        }
754    }
755}
756
757impl<'a> FromKclValue<'a> for FaceTag {
758    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
759        let case1 = || match arg.as_str() {
760            Some("start" | "START") => Some(Self::StartOrEnd(super::sketch::StartOrEnd::Start)),
761            Some("end" | "END") => Some(Self::StartOrEnd(super::sketch::StartOrEnd::End)),
762            _ => None,
763        };
764        let case2 = || {
765            let tag = TagIdentifier::from_kcl_val(arg)?;
766            Some(Self::Tag(Box::new(tag)))
767        };
768        case1().or_else(case2)
769    }
770}
771
772impl<'a> FromKclValue<'a> for super::faces::FaceSpecifier {
773    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
774        FaceTag::from_kcl_val(arg)
775            .map(super::faces::FaceSpecifier::FaceTag)
776            .or_else(|| {
777                crate::execution::Segment::from_kcl_val(arg)
778                    .map(Box::new)
779                    .map(super::faces::FaceSpecifier::Segment)
780            })
781    }
782}
783
784impl<'a> FromKclValue<'a> for crate::execution::Segment {
785    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
786        match arg {
787            KclValue::Segment { value } => match &value.repr {
788                crate::execution::SegmentRepr::Unsolved { .. } => None,
789                crate::execution::SegmentRepr::Solved { segment, .. } => Some(segment.as_ref().to_owned()),
790            },
791            _ => None,
792        }
793    }
794}
795
796impl<'a> FromKclValue<'a> for super::sketch::TangentialArcData {
797    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
798        let obj = arg.as_object()?;
799        let_field_of!(obj, radius);
800        let_field_of!(obj, offset);
801        Some(Self::RadiusAndOffset { radius, offset })
802    }
803}
804
805impl<'a> FromKclValue<'a> for crate::execution::Point3d {
806    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
807        // Case 1: object with x/y/z fields
808        if let Some(obj) = arg.as_object() {
809            let_field_of!(obj, x, TyF64);
810            let_field_of!(obj, y, TyF64);
811            let_field_of!(obj, z, TyF64);
812            // TODO here and below we could use coercing combination.
813            let (a, ty) = NumericType::combine_eq_array(&[x, y, z]);
814            return Some(Self {
815                x: a[0],
816                y: a[1],
817                z: a[2],
818                units: ty.as_length(),
819            });
820        }
821        // Case 2: Array of 3 numbers.
822        let [x, y, z]: [TyF64; 3] = FromKclValue::from_kcl_val(arg)?;
823        let (a, ty) = NumericType::combine_eq_array(&[x, y, z]);
824        Some(Self {
825            x: a[0],
826            y: a[1],
827            z: a[2],
828            units: ty.as_length(),
829        })
830    }
831}
832
833impl<'a> FromKclValue<'a> for super::sketch::PlaneData {
834    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
835        // Case 0: actual plane
836        if let KclValue::Plane { value } = arg {
837            return Some(Self::Plane(PlaneInfo {
838                origin: value.info.origin,
839                x_axis: value.info.x_axis,
840                y_axis: value.info.y_axis,
841                z_axis: value.info.z_axis,
842            }));
843        }
844        // Case 1: predefined plane
845        if let Some(s) = arg.as_str() {
846            return match s {
847                "XY" | "xy" => Some(Self::XY),
848                "-XY" | "-xy" => Some(Self::NegXY),
849                "XZ" | "xz" => Some(Self::XZ),
850                "-XZ" | "-xz" => Some(Self::NegXZ),
851                "YZ" | "yz" => Some(Self::YZ),
852                "-YZ" | "-yz" => Some(Self::NegYZ),
853                _ => None,
854            };
855        }
856        // Case 2: custom plane
857        let obj = arg.as_object()?;
858        let_field_of!(obj, plane, &KclObjectFields);
859        let origin = plane.get("origin").and_then(FromKclValue::from_kcl_val)?;
860        let x_axis: crate::execution::Point3d = plane.get("xAxis").and_then(FromKclValue::from_kcl_val)?;
861        let y_axis = plane.get("yAxis").and_then(FromKclValue::from_kcl_val)?;
862        let z_axis = x_axis.axes_cross_product(&y_axis);
863        Some(Self::Plane(PlaneInfo {
864            origin,
865            x_axis,
866            y_axis,
867            z_axis,
868        }))
869    }
870}
871
872impl<'a> FromKclValue<'a> for crate::execution::ExtrudePlane {
873    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
874        let obj = arg.as_object()?;
875        let_field_of!(obj, face_id "faceId");
876        let tag = FromKclValue::from_kcl_val(obj.get("tag")?);
877        let_field_of!(obj, geo_meta "geoMeta");
878        Some(Self { face_id, tag, geo_meta })
879    }
880}
881
882impl<'a> FromKclValue<'a> for crate::execution::ExtrudeArc {
883    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
884        let obj = arg.as_object()?;
885        let_field_of!(obj, face_id "faceId");
886        let tag = FromKclValue::from_kcl_val(obj.get("tag")?);
887        let_field_of!(obj, geo_meta "geoMeta");
888        Some(Self { face_id, tag, geo_meta })
889    }
890}
891
892impl<'a> FromKclValue<'a> for crate::execution::GeoMeta {
893    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
894        let obj = arg.as_object()?;
895        let_field_of!(obj, id);
896        let_field_of!(obj, source_range "sourceRange");
897        Some(Self {
898            id,
899            metadata: Metadata { source_range },
900        })
901    }
902}
903
904impl<'a> FromKclValue<'a> for crate::execution::ChamferSurface {
905    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
906        let obj = arg.as_object()?;
907        let_field_of!(obj, face_id "faceId");
908        let tag = FromKclValue::from_kcl_val(obj.get("tag")?);
909        let_field_of!(obj, geo_meta "geoMeta");
910        Some(Self { face_id, tag, geo_meta })
911    }
912}
913
914impl<'a> FromKclValue<'a> for crate::execution::FilletSurface {
915    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
916        let obj = arg.as_object()?;
917        let_field_of!(obj, face_id "faceId");
918        let tag = FromKclValue::from_kcl_val(obj.get("tag")?);
919        let_field_of!(obj, geo_meta "geoMeta");
920        Some(Self { face_id, tag, geo_meta })
921    }
922}
923
924impl<'a> FromKclValue<'a> for ExtrudeSurface {
925    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
926        let case1 = crate::execution::ExtrudePlane::from_kcl_val;
927        let case2 = crate::execution::ExtrudeArc::from_kcl_val;
928        let case3 = crate::execution::ChamferSurface::from_kcl_val;
929        let case4 = crate::execution::FilletSurface::from_kcl_val;
930        case1(arg)
931            .map(Self::ExtrudePlane)
932            .or_else(|| case2(arg).map(Self::ExtrudeArc))
933            .or_else(|| case3(arg).map(Self::Chamfer))
934            .or_else(|| case4(arg).map(Self::Fillet))
935    }
936}
937
938impl<'a> FromKclValue<'a> for crate::execution::EdgeCut {
939    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
940        let obj = arg.as_object()?;
941        let_field_of!(obj, typ "type");
942        let tag = Box::new(obj.get("tag").and_then(FromKclValue::from_kcl_val));
943        let_field_of!(obj, edge_id "edgeId");
944        let_field_of!(obj, id);
945        match typ {
946            "fillet" => {
947                let_field_of!(obj, radius);
948                Some(Self::Fillet {
949                    edge_id,
950                    tag,
951                    id,
952                    radius,
953                })
954            }
955            "chamfer" => {
956                let_field_of!(obj, length);
957                Some(Self::Chamfer {
958                    id,
959                    length,
960                    edge_id,
961                    tag,
962                })
963            }
964            _ => None,
965        }
966    }
967}
968
969macro_rules! impl_from_kcl_for_vec {
970    ($typ:path) => {
971        impl<'a> FromKclValue<'a> for Vec<$typ> {
972            fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
973                arg.clone()
974                    .into_array()
975                    .iter()
976                    .map(|value| FromKclValue::from_kcl_val(value))
977                    .collect::<Option<_>>()
978            }
979        }
980    };
981}
982
983impl_from_kcl_for_vec!(FaceTag);
984impl_from_kcl_for_vec!(crate::execution::EdgeCut);
985impl_from_kcl_for_vec!(crate::execution::Metadata);
986impl_from_kcl_for_vec!(super::fillet::EdgeReference);
987impl_from_kcl_for_vec!(ExtrudeSurface);
988impl_from_kcl_for_vec!(Segment);
989impl_from_kcl_for_vec!(TyF64);
990impl_from_kcl_for_vec!(Solid);
991impl_from_kcl_for_vec!(Sketch);
992impl_from_kcl_for_vec!(crate::execution::GdtAnnotation);
993impl_from_kcl_for_vec!(crate::execution::GeometryWithImportedGeometry);
994impl_from_kcl_for_vec!(crate::execution::BoundedEdge);
995impl_from_kcl_for_vec!(String);
996
997impl<'a> FromKclValue<'a> for SourceRange {
998    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
999        let value = match arg {
1000            KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } => value,
1001            _ => {
1002                return None;
1003            }
1004        };
1005        let [v0, v1, v2] = value.as_slice() else {
1006            return None;
1007        };
1008        Some(SourceRange::new(
1009            v0.as_usize()?,
1010            v1.as_usize()?,
1011            ModuleId::from_usize(v2.as_usize()?),
1012        ))
1013    }
1014}
1015
1016impl<'a> FromKclValue<'a> for crate::execution::Metadata {
1017    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1018        FromKclValue::from_kcl_val(arg).map(|sr| Self { source_range: sr })
1019    }
1020}
1021
1022impl<'a> FromKclValue<'a> for crate::execution::Solid {
1023    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1024        arg.as_solid().cloned()
1025    }
1026}
1027
1028impl<'a> FromKclValue<'a> for crate::execution::GdtAnnotation {
1029    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1030        let KclValue::GdtAnnotation { value } = arg else {
1031            return None;
1032        };
1033        Some(value.as_ref().to_owned())
1034    }
1035}
1036
1037impl<'a> FromKclValue<'a> for crate::execution::SolidOrSketchOrImportedGeometry {
1038    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1039        match arg {
1040            KclValue::Solid { value } => Some(Self::SolidSet(vec![(**value).clone()])),
1041            KclValue::Sketch { value } => Some(Self::SketchSet(vec![(**value).clone()])),
1042            KclValue::HomArray { value, .. } => {
1043                let mut solids = vec![];
1044                let mut sketches = vec![];
1045                for item in value {
1046                    match item {
1047                        KclValue::Solid { value } => solids.push((**value).clone()),
1048                        KclValue::Sketch { value } => sketches.push((**value).clone()),
1049                        _ => return None,
1050                    }
1051                }
1052                if !solids.is_empty() {
1053                    Some(Self::SolidSet(solids))
1054                } else {
1055                    Some(Self::SketchSet(sketches))
1056                }
1057            }
1058            KclValue::ImportedGeometry(value) => Some(Self::ImportedGeometry(Box::new(value.clone()))),
1059            _ => None,
1060        }
1061    }
1062}
1063
1064impl<'a> FromKclValue<'a> for crate::execution::HideableGeometry {
1065    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1066        match arg {
1067            KclValue::Solid { value } => Some(Self::SolidSet(vec![(**value).clone()])),
1068            KclValue::Plane { value } => Some(Self::PlaneSet(vec![(**value).clone()])),
1069            KclValue::Sketch { value } => Some(Self::SketchSet(vec![(**value).clone()])),
1070            KclValue::Helix { value } => Some(Self::HelixSet(vec![(**value).clone()])),
1071            KclValue::GdtAnnotation { value } => Some(Self::GdtAnnotationSet(vec![(**value).clone()])),
1072            KclValue::HomArray { value, .. } => {
1073                let mut solids = vec![];
1074                let mut planes = vec![];
1075                let mut sketches = vec![];
1076                let mut helices = vec![];
1077                let mut annotations = vec![];
1078                for item in value {
1079                    match item {
1080                        KclValue::Solid { value } => solids.push((**value).clone()),
1081                        KclValue::Plane { value } => planes.push((**value).clone()),
1082                        KclValue::Sketch { value } => sketches.push((**value).clone()),
1083                        KclValue::Helix { value } => helices.push((**value).clone()),
1084                        KclValue::GdtAnnotation { value } => annotations.push((**value).clone()),
1085                        _ => return None,
1086                    }
1087                }
1088                if !solids.is_empty() {
1089                    Some(Self::SolidSet(solids))
1090                } else if !planes.is_empty() {
1091                    Some(Self::PlaneSet(planes))
1092                } else if !sketches.is_empty() {
1093                    Some(Self::SketchSet(sketches))
1094                } else if !helices.is_empty() {
1095                    Some(Self::HelixSet(helices))
1096                } else {
1097                    Some(Self::GdtAnnotationSet(annotations))
1098                }
1099            }
1100            KclValue::ImportedGeometry(value) => Some(Self::ImportedGeometry(Box::new(value.clone()))),
1101            _ => None,
1102        }
1103    }
1104}
1105
1106impl<'a> FromKclValue<'a> for crate::execution::SolidOrImportedGeometry {
1107    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1108        match arg {
1109            KclValue::Solid { value } => Some(Self::SolidSet(vec![(**value).clone()])),
1110            KclValue::HomArray { value, .. } => {
1111                let mut solids = vec![];
1112                for item in value {
1113                    match item {
1114                        KclValue::Solid { value } => solids.push((**value).clone()),
1115                        _ => return None,
1116                    }
1117                }
1118                Some(Self::SolidSet(solids))
1119            }
1120            KclValue::ImportedGeometry(value) => Some(Self::ImportedGeometry(Box::new(value.clone()))),
1121            _ => None,
1122        }
1123    }
1124}
1125
1126impl<'a> FromKclValue<'a> for super::sketch::SketchData {
1127    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1128        // Order is critical since PlaneData is a subset of Plane.
1129        let case1 = crate::execution::Plane::from_kcl_val;
1130        let case2 = super::sketch::PlaneData::from_kcl_val;
1131        let case3 = crate::execution::Solid::from_kcl_val;
1132        let case4 = <Vec<Solid>>::from_kcl_val;
1133        case1(arg)
1134            .map(Box::new)
1135            .map(Self::Plane)
1136            .or_else(|| case2(arg).map(Self::PlaneOrientation))
1137            .or_else(|| case3(arg).map(Box::new).map(Self::Solid))
1138            .or_else(|| case4(arg).map(|v| Box::new(v[0].clone())).map(Self::Solid))
1139    }
1140}
1141
1142impl<'a> FromKclValue<'a> for super::fillet::EdgeReference {
1143    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1144        let id = arg.as_uuid().map(Self::Uuid);
1145        let tag = || TagIdentifier::from_kcl_val(arg).map(Box::new).map(Self::Tag);
1146        id.or_else(tag)
1147    }
1148}
1149
1150impl<'a> FromKclValue<'a> for super::axis_or_reference::Axis2dOrEdgeReference {
1151    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1152        let case1 = |arg: &KclValue| {
1153            let obj = arg.as_object()?;
1154            let_field_of!(obj, direction);
1155            let_field_of!(obj, origin);
1156            Some(Self::Axis { direction, origin })
1157        };
1158        let case2 = super::fillet::EdgeReference::from_kcl_val;
1159        let case3 = Segment::from_kcl_val;
1160        case1(arg)
1161            .or_else(|| case2(arg).map(Self::Edge))
1162            .or_else(|| case3(arg).and_then(|seg| Self::from_segment(&seg).ok()))
1163    }
1164}
1165
1166impl<'a> FromKclValue<'a> for super::axis_or_reference::Axis3dOrEdgeReference {
1167    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1168        let case1 = |arg: &KclValue| {
1169            let obj = arg.as_object()?;
1170            let_field_of!(obj, direction);
1171            let_field_of!(obj, origin);
1172            Some(Self::Axis { direction, origin })
1173        };
1174        let case2 = super::fillet::EdgeReference::from_kcl_val;
1175        let case3 = Segment::from_kcl_val;
1176        case1(arg)
1177            .or_else(|| case2(arg).map(Self::Edge))
1178            .or_else(|| case3(arg).and_then(|seg| Self::from_segment(&seg).ok()))
1179    }
1180}
1181
1182impl<'a> FromKclValue<'a> for super::axis_or_reference::Point3dOrEdgeReference {
1183    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1184        let case1 = <[TyF64; 3]>::from_kcl_val;
1185        let case2 = super::fillet::EdgeReference::from_kcl_val;
1186        let case3 = Segment::from_kcl_val;
1187        case1(arg)
1188            .map(Self::Point)
1189            .or_else(|| case2(arg).map(Self::Edge))
1190            .or_else(|| case3(arg).and_then(|seg| Self::from_segment(&seg).ok()))
1191    }
1192}
1193
1194impl<'a> FromKclValue<'a> for super::axis_or_reference::MirrorAcross3d {
1195    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1196        let case1 = crate::execution::Plane::from_kcl_val;
1197        let case2 = |arg: &KclValue| {
1198            let obj = arg.as_object()?;
1199            let_field_of!(obj, direction);
1200            let_field_of!(obj, origin);
1201            Some(Self::Axis {
1202                direction: Box::new(direction),
1203                origin: Box::new(origin),
1204            })
1205        };
1206        let case3 = super::fillet::EdgeReference::from_kcl_val;
1207        let case4 = Segment::from_kcl_val;
1208        case1(arg)
1209            .map(|p| Self::Plane(Box::new(p)))
1210            .or_else(|| case2(arg))
1211            .or_else(|| case3(arg).map(|e| Self::Edge(Box::new(e))))
1212            .or_else(|| case4(arg).and_then(|seg| Self::from_segment(&seg).ok()))
1213    }
1214}
1215
1216impl<'a> FromKclValue<'a> for super::axis_or_reference::Axis2dOrPoint2d {
1217    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1218        let case1 = |arg: &KclValue| {
1219            let obj = arg.as_object()?;
1220            let_field_of!(obj, direction);
1221            let_field_of!(obj, origin);
1222            Some(Self::Axis { direction, origin })
1223        };
1224        let case2 = <[TyF64; 2]>::from_kcl_val;
1225        case1(arg).or_else(|| case2(arg).map(Self::Point))
1226    }
1227}
1228
1229impl<'a> FromKclValue<'a> for super::axis_or_reference::Axis3dOrPoint3d {
1230    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1231        let case1 = |arg: &KclValue| {
1232            let obj = arg.as_object()?;
1233            let_field_of!(obj, direction);
1234            let_field_of!(obj, origin);
1235            Some(Self::Axis { direction, origin })
1236        };
1237        let case2 = <[TyF64; 3]>::from_kcl_val;
1238        case1(arg).or_else(|| case2(arg).map(Self::Point))
1239    }
1240}
1241
1242impl<'a> FromKclValue<'a> for super::axis_or_reference::Point3dAxis3dOrGeometryReference {
1243    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1244        let case1 = |arg: &KclValue| {
1245            let obj = arg.as_object()?;
1246            let_field_of!(obj, direction);
1247            let_field_of!(obj, origin);
1248            Some(Self::Axis { direction, origin })
1249        };
1250        let case2 = <[TyF64; 3]>::from_kcl_val;
1251        let case3 = super::fillet::EdgeReference::from_kcl_val;
1252        let case4 = FaceTag::from_kcl_val;
1253        let case5 = Box::<Solid>::from_kcl_val;
1254        let case6 = TagIdentifier::from_kcl_val;
1255        let case7 = Box::<Plane>::from_kcl_val;
1256        let case8 = Box::<Sketch>::from_kcl_val;
1257
1258        case1(arg)
1259            .or_else(|| case2(arg).map(Self::Point))
1260            .or_else(|| case3(arg).map(Self::Edge))
1261            .or_else(|| case4(arg).map(Self::Face))
1262            .or_else(|| case5(arg).map(Self::Solid))
1263            .or_else(|| case6(arg).map(Self::TaggedEdgeOrFace))
1264            .or_else(|| case7(arg).map(Self::Plane))
1265            .or_else(|| case8(arg).map(Self::Sketch))
1266    }
1267}
1268
1269impl<'a> FromKclValue<'a> for Box<Face> {
1270    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1271        let KclValue::Face { value } = arg else {
1272            return None;
1273        };
1274        Some(value.to_owned())
1275    }
1276}
1277
1278impl<'a> FromKclValue<'a> for Extrudable {
1279    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1280        let case1 = Box::<Sketch>::from_kcl_val;
1281        let case2 = FaceTag::from_kcl_val;
1282        let case3 = Box::<Face>::from_kcl_val;
1283        let case4 = Uuid::from_kcl_val;
1284        let case5 = Box::<TagIdentifier>::from_kcl_val;
1285        case1(arg)
1286            .map(Self::Sketch)
1287            .or_else(|| case2(arg).map(Self::FaceTag))
1288            .or_else(|| case3(arg).map(Self::Face))
1289            .or_else(|| case4(arg).map(Self::Edge))
1290            .or_else(|| case5(arg).map(Self::EdgeTag))
1291    }
1292}
1293
1294impl<'a> FromKclValue<'a> for i64 {
1295    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1296        match arg {
1297            KclValue::Number { value, .. } => crate::try_f64_to_i64(*value),
1298            _ => None,
1299        }
1300    }
1301}
1302
1303impl<'a> FromKclValue<'a> for &'a str {
1304    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1305        let KclValue::String { value, meta: _ } = arg else {
1306            return None;
1307        };
1308        Some(value)
1309    }
1310}
1311
1312impl<'a> FromKclValue<'a> for &'a KclObjectFields {
1313    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1314        let KclValue::Object { value, .. } = arg else {
1315            return None;
1316        };
1317        Some(value)
1318    }
1319}
1320
1321impl<'a> FromKclValue<'a> for uuid::Uuid {
1322    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1323        let KclValue::Uuid { value, meta: _ } = arg else {
1324            return None;
1325        };
1326        Some(*value)
1327    }
1328}
1329
1330impl<'a> FromKclValue<'a> for u32 {
1331    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1332        match arg {
1333            KclValue::Number { value, .. } => crate::try_f64_to_u32(*value),
1334            _ => None,
1335        }
1336    }
1337}
1338
1339impl<'a> FromKclValue<'a> for NonZeroU32 {
1340    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1341        u32::from_kcl_val(arg).and_then(|x| x.try_into().ok())
1342    }
1343}
1344
1345impl<'a> FromKclValue<'a> for u64 {
1346    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1347        match arg {
1348            KclValue::Number { value, .. } => crate::try_f64_to_u64(*value),
1349            _ => None,
1350        }
1351    }
1352}
1353
1354impl<'a> FromKclValue<'a> for TyF64 {
1355    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1356        match arg {
1357            KclValue::Number { value, ty, .. } => Some(TyF64::new(*value, *ty)),
1358            _ => None,
1359        }
1360    }
1361}
1362
1363impl<'a> FromKclValue<'a> for [TyF64; 2] {
1364    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1365        match arg {
1366            KclValue::Tuple { value, meta: _ } | KclValue::HomArray { value, .. } => {
1367                let [v0, v1] = value.as_slice() else {
1368                    return None;
1369                };
1370                let array = [v0.as_ty_f64()?, v1.as_ty_f64()?];
1371                Some(array)
1372            }
1373            _ => None,
1374        }
1375    }
1376}
1377
1378impl<'a> FromKclValue<'a> for [TyF64; 3] {
1379    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1380        match arg {
1381            KclValue::Tuple { value, meta: _ } | KclValue::HomArray { value, .. } => {
1382                let [v0, v1, v2] = value.as_slice() else {
1383                    return None;
1384                };
1385                let array = [v0.as_ty_f64()?, v1.as_ty_f64()?, v2.as_ty_f64()?];
1386                Some(array)
1387            }
1388            _ => None,
1389        }
1390    }
1391}
1392
1393impl<'a> FromKclValue<'a> for [TyF64; 6] {
1394    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1395        match arg {
1396            KclValue::Tuple { value, meta: _ } | KclValue::HomArray { value, .. } => {
1397                let [v0, v1, v2, v3, v4, v5] = value.as_slice() else {
1398                    return None;
1399                };
1400                let array = [
1401                    v0.as_ty_f64()?,
1402                    v1.as_ty_f64()?,
1403                    v2.as_ty_f64()?,
1404                    v3.as_ty_f64()?,
1405                    v4.as_ty_f64()?,
1406                    v5.as_ty_f64()?,
1407                ];
1408                Some(array)
1409            }
1410            _ => None,
1411        }
1412    }
1413}
1414
1415impl<'a> FromKclValue<'a> for Sketch {
1416    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1417        let KclValue::Sketch { value } = arg else {
1418            return None;
1419        };
1420        Some(value.as_ref().to_owned())
1421    }
1422}
1423
1424impl<'a> FromKclValue<'a> for Helix {
1425    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1426        let KclValue::Helix { value } = arg else {
1427            return None;
1428        };
1429        Some(value.as_ref().to_owned())
1430    }
1431}
1432
1433impl<'a> FromKclValue<'a> for SweepPath {
1434    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1435        let case1 = Sketch::from_kcl_val;
1436        let case2 = <Vec<Sketch>>::from_kcl_val;
1437        let case3 = Helix::from_kcl_val;
1438        let case4 = <Vec<Segment>>::from_kcl_val;
1439        case1(arg)
1440            .map(Self::Sketch)
1441            .or_else(|| case2(arg).map(|arg0: Vec<Sketch>| Self::Sketch(arg0[0].clone())))
1442            .or_else(|| case3(arg).map(|arg0: Helix| Self::Helix(Box::new(arg0))))
1443            .or_else(|| case4(arg).map(Self::Segments))
1444    }
1445}
1446impl<'a> FromKclValue<'a> for String {
1447    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1448        let KclValue::String { value, meta: _ } = arg else {
1449            return None;
1450        };
1451        Some(value.to_owned())
1452    }
1453}
1454impl<'a> FromKclValue<'a> for crate::parsing::ast::types::KclNone {
1455    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1456        let KclValue::KclNone { value, meta: _ } = arg else {
1457            return None;
1458        };
1459        Some(value.to_owned())
1460    }
1461}
1462impl<'a> FromKclValue<'a> for bool {
1463    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1464        let KclValue::Bool { value, meta: _ } = arg else {
1465            return None;
1466        };
1467        Some(*value)
1468    }
1469}
1470
1471impl<'a> FromKclValue<'a> for Box<Solid> {
1472    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1473        let KclValue::Solid { value } = arg else {
1474            return None;
1475        };
1476        Some(value.to_owned())
1477    }
1478}
1479
1480impl<'a> FromKclValue<'a> for BoundedEdge {
1481    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1482        let KclValue::BoundedEdge { value, .. } = arg else {
1483            return None;
1484        };
1485        Some(value.to_owned())
1486    }
1487}
1488
1489impl<'a> FromKclValue<'a> for Box<Plane> {
1490    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1491        let KclValue::Plane { value } = arg else {
1492            return None;
1493        };
1494        Some(value.to_owned())
1495    }
1496}
1497
1498impl<'a> FromKclValue<'a> for Box<Sketch> {
1499    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1500        let KclValue::Sketch { value } = arg else {
1501            return None;
1502        };
1503        Some(value.to_owned())
1504    }
1505}
1506
1507impl<'a> FromKclValue<'a> for Box<TagIdentifier> {
1508    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1509        let KclValue::TagIdentifier(value) = arg else {
1510            return None;
1511        };
1512        Some(value.to_owned())
1513    }
1514}
1515
1516impl<'a> FromKclValue<'a> for FunctionSource {
1517    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1518        arg.as_function().cloned()
1519    }
1520}
1521
1522impl<'a> FromKclValue<'a> for SketchOrSurface {
1523    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1524        match arg {
1525            KclValue::Sketch { value: sg } => Some(Self::Sketch(sg.to_owned())),
1526            KclValue::Plane { value } => Some(Self::SketchSurface(SketchSurface::Plane(value.clone()))),
1527            KclValue::Face { value } => Some(Self::SketchSurface(SketchSurface::Face(value.clone()))),
1528            _ => None,
1529        }
1530    }
1531}
1532impl<'a> FromKclValue<'a> for SketchSurface {
1533    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1534        match arg {
1535            KclValue::Plane { value } => Some(Self::Plane(value.clone())),
1536            KclValue::Face { value } => Some(Self::Face(value.clone())),
1537            _ => None,
1538        }
1539    }
1540}
1541
1542impl From<Args> for Metadata {
1543    fn from(value: Args) -> Self {
1544        Self {
1545            source_range: value.source_range,
1546        }
1547    }
1548}
1549
1550impl From<Args> for Vec<Metadata> {
1551    fn from(value: Args) -> Self {
1552        vec![Metadata {
1553            source_range: value.source_range,
1554        }]
1555    }
1556}