Skip to main content

kcl_lib/std/
args.rs

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