kcl_lib/std/
args.rs

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