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