Skip to main content

kcl_lib/std/
args.rs

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