Skip to main content

kcl_lib/std/
args.rs

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