Skip to main content

kcl_lib/std/
args.rs

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