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