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