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