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