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