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