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