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