kcl_lib/std/
args.rs

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