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