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.stack().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                        .stack()
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::MixedArray {
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::MixedArray {
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_sketches(&self) -> Result<(SketchSet, Sketch), KclError> {
448        FromArgs::from_args(self, 0)
449    }
450
451    pub(crate) fn get_sketch(&self) -> Result<Sketch, KclError> {
452        FromArgs::from_args(self, 0)
453    }
454
455    pub(crate) fn get_data<'a, T>(&'a self) -> Result<T, KclError>
456    where
457        T: FromArgs<'a> + serde::de::DeserializeOwned,
458    {
459        FromArgs::from_args(self, 0)
460    }
461
462    pub(crate) fn get_import_data(&self) -> Result<(String, Option<crate::std::import::ImportFormat>), KclError> {
463        FromArgs::from_args(self, 0)
464    }
465
466    pub(crate) fn get_data_and_optional_tag<'a, T>(&'a self) -> Result<(T, Option<FaceTag>), KclError>
467    where
468        T: serde::de::DeserializeOwned + FromKclValue<'a> + Sized,
469    {
470        FromArgs::from_args(self, 0)
471    }
472
473    pub(crate) fn get_data_and_sketch<'a, T>(&'a self) -> Result<(T, Sketch), KclError>
474    where
475        T: serde::de::DeserializeOwned + FromArgs<'a>,
476    {
477        FromArgs::from_args(self, 0)
478    }
479
480    pub(crate) fn get_data_and_sketch_set<'a, T>(&'a self) -> Result<(T, SketchSet), KclError>
481    where
482        T: serde::de::DeserializeOwned + FromArgs<'a>,
483    {
484        FromArgs::from_args(self, 0)
485    }
486
487    pub(crate) fn get_data_and_sketch_and_tag<'a, T>(&'a self) -> Result<(T, Sketch, Option<TagNode>), KclError>
488    where
489        T: serde::de::DeserializeOwned + FromKclValue<'a> + Sized,
490    {
491        FromArgs::from_args(self, 0)
492    }
493
494    pub(crate) fn get_data_and_sketch_surface<'a, T>(&'a self) -> Result<(T, SketchSurface, Option<TagNode>), KclError>
495    where
496        T: serde::de::DeserializeOwned + FromKclValue<'a> + Sized,
497    {
498        FromArgs::from_args(self, 0)
499    }
500
501    pub(crate) fn get_data_and_solid<'a, T>(&'a self) -> Result<(T, Box<Solid>), KclError>
502    where
503        T: serde::de::DeserializeOwned + FromKclValue<'a> + Sized,
504    {
505        FromArgs::from_args(self, 0)
506    }
507
508    pub(crate) fn get_tag_to_number_sketch(&self) -> Result<(TagIdentifier, f64, Sketch), KclError> {
509        FromArgs::from_args(self, 0)
510    }
511
512    pub(crate) async fn get_adjacent_face_to_tag(
513        &self,
514        exec_state: &mut ExecState,
515        tag: &TagIdentifier,
516        must_be_planar: bool,
517    ) -> Result<uuid::Uuid, KclError> {
518        if tag.value.is_empty() {
519            return Err(KclError::Type(KclErrorDetails {
520                message: "Expected a non-empty tag for the face".to_string(),
521                source_ranges: vec![self.source_range],
522            }));
523        }
524
525        let engine_info = self.get_tag_engine_info_check_surface(exec_state, tag)?;
526
527        let surface = engine_info.surface.as_ref().ok_or_else(|| {
528            KclError::Type(KclErrorDetails {
529                message: format!("Tag `{}` does not have a surface", tag.value),
530                source_ranges: vec![self.source_range],
531            })
532        })?;
533
534        if let Some(face_from_surface) = match surface {
535            ExtrudeSurface::ExtrudePlane(extrude_plane) => {
536                if let Some(plane_tag) = &extrude_plane.tag {
537                    if plane_tag.name == tag.value {
538                        Some(Ok(extrude_plane.face_id))
539                    } else {
540                        None
541                    }
542                } else {
543                    None
544                }
545            }
546            // The must be planar check must be called before the arc check.
547            ExtrudeSurface::ExtrudeArc(_) if must_be_planar => Some(Err(KclError::Type(KclErrorDetails {
548                message: format!("Tag `{}` is a non-planar surface", tag.value),
549                source_ranges: vec![self.source_range],
550            }))),
551            ExtrudeSurface::ExtrudeArc(extrude_arc) => {
552                if let Some(arc_tag) = &extrude_arc.tag {
553                    if arc_tag.name == tag.value {
554                        Some(Ok(extrude_arc.face_id))
555                    } else {
556                        None
557                    }
558                } else {
559                    None
560                }
561            }
562            ExtrudeSurface::Chamfer(chamfer) => {
563                if let Some(chamfer_tag) = &chamfer.tag {
564                    if chamfer_tag.name == tag.value {
565                        Some(Ok(chamfer.face_id))
566                    } else {
567                        None
568                    }
569                } else {
570                    None
571                }
572            }
573            // The must be planar check must be called before the fillet check.
574            ExtrudeSurface::Fillet(_) if must_be_planar => Some(Err(KclError::Type(KclErrorDetails {
575                message: format!("Tag `{}` is a non-planar surface", tag.value),
576                source_ranges: vec![self.source_range],
577            }))),
578            ExtrudeSurface::Fillet(fillet) => {
579                if let Some(fillet_tag) = &fillet.tag {
580                    if fillet_tag.name == tag.value {
581                        Some(Ok(fillet.face_id))
582                    } else {
583                        None
584                    }
585                } else {
586                    None
587                }
588            }
589        } {
590            return face_from_surface;
591        }
592
593        // If we still haven't found the face, return an error.
594        Err(KclError::Type(KclErrorDetails {
595            message: format!("Expected a face with the tag `{}`", tag.value),
596            source_ranges: vec![self.source_range],
597        }))
598    }
599
600    pub(crate) fn get_polygon_args(
601        &self,
602    ) -> Result<
603        (
604            crate::std::shapes::PolygonData,
605            crate::std::shapes::SketchOrSurface,
606            Option<TagNode>,
607        ),
608        KclError,
609    > {
610        FromArgs::from_args(self, 0)
611    }
612}
613
614/// Types which impl this trait can be read out of the `Args` passed into a KCL function.
615pub trait FromArgs<'a>: Sized {
616    /// Get this type from the args passed into a KCL function, at the given index in the argument list.
617    fn from_args(args: &'a Args, index: usize) -> Result<Self, KclError>;
618}
619
620/// Types which impl this trait can be extracted from a `KclValue`.
621pub trait FromKclValue<'a>: Sized {
622    /// Try to convert a KclValue into this type.
623    fn from_kcl_val(arg: &'a KclValue) -> Option<Self>;
624}
625
626impl<'a> FromArgs<'a> for Vec<KclValue> {
627    fn from_args(args: &'a Args, i: usize) -> Result<Self, KclError> {
628        let Some(arg) = args.args.get(i) else {
629            return Err(KclError::Semantic(KclErrorDetails {
630                message: format!("Expected an argument at index {i}"),
631                source_ranges: vec![args.source_range],
632            }));
633        };
634        let KclValue::MixedArray { value: array, meta: _ } = &arg.value else {
635            let message = format!("Expected an array but found {}", arg.value.human_friendly_type());
636            return Err(KclError::Type(KclErrorDetails {
637                source_ranges: arg.source_ranges(),
638                message,
639            }));
640        };
641        Ok(array.to_owned())
642    }
643}
644
645impl<'a, T> FromArgs<'a> for T
646where
647    T: FromKclValue<'a> + Sized,
648{
649    fn from_args(args: &'a Args, i: usize) -> Result<Self, KclError> {
650        let Some(arg) = args.args.get(i) else {
651            return Err(KclError::Semantic(KclErrorDetails {
652                message: format!("Expected an argument at index {i}"),
653                source_ranges: vec![args.source_range],
654            }));
655        };
656        let Some(val) = T::from_kcl_val(&arg.value) else {
657            return Err(KclError::Semantic(KclErrorDetails {
658                message: format!(
659                    "Argument at index {i} was supposed to be type {} but found {}",
660                    type_name::<T>(),
661                    arg.value.human_friendly_type(),
662                ),
663                source_ranges: arg.source_ranges(),
664            }));
665        };
666        Ok(val)
667    }
668}
669
670impl<'a, T> FromArgs<'a> for Option<T>
671where
672    T: FromKclValue<'a> + Sized,
673{
674    fn from_args(args: &'a Args, i: usize) -> Result<Self, KclError> {
675        let Some(arg) = args.args.get(i) else { return Ok(None) };
676        if crate::parsing::ast::types::KclNone::from_kcl_val(&arg.value).is_some() {
677            return Ok(None);
678        }
679        let Some(val) = T::from_kcl_val(&arg.value) else {
680            return Err(KclError::Semantic(KclErrorDetails {
681                message: format!(
682                    "Argument at index {i} was supposed to be type Option<{}> but found {}",
683                    type_name::<T>(),
684                    arg.value.human_friendly_type()
685                ),
686                source_ranges: arg.source_ranges(),
687            }));
688        };
689        Ok(Some(val))
690    }
691}
692
693impl<'a, A, B> FromArgs<'a> for (A, B)
694where
695    A: FromArgs<'a>,
696    B: FromArgs<'a>,
697{
698    fn from_args(args: &'a Args, i: usize) -> Result<Self, KclError> {
699        let a = A::from_args(args, i)?;
700        let b = B::from_args(args, i + 1)?;
701        Ok((a, b))
702    }
703}
704
705impl<'a, A, B, C> FromArgs<'a> for (A, B, C)
706where
707    A: FromArgs<'a>,
708    B: FromArgs<'a>,
709    C: 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        let c = C::from_args(args, i + 2)?;
715        Ok((a, b, c))
716    }
717}
718impl<'a, A, B, C, D> FromArgs<'a> for (A, B, C, D)
719where
720    A: FromArgs<'a>,
721    B: FromArgs<'a>,
722    C: FromArgs<'a>,
723    D: FromArgs<'a>,
724{
725    fn from_args(args: &'a Args, i: usize) -> Result<Self, KclError> {
726        let a = A::from_args(args, i)?;
727        let b = B::from_args(args, i + 1)?;
728        let c = C::from_args(args, i + 2)?;
729        let d = D::from_args(args, i + 3)?;
730        Ok((a, b, c, d))
731    }
732}
733
734impl<'a> FromKclValue<'a> for [f64; 2] {
735    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
736        let KclValue::MixedArray { value, meta: _ } = arg else {
737            return None;
738        };
739        if value.len() != 2 {
740            return None;
741        }
742        let v0 = value.first()?;
743        let v1 = value.get(1)?;
744        let array = [v0.as_f64()?, v1.as_f64()?];
745        Some(array)
746    }
747}
748
749impl<'a> FromKclValue<'a> for [usize; 3] {
750    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
751        let KclValue::MixedArray { value, meta: _ } = arg else {
752            return None;
753        };
754        if value.len() != 3 {
755            return None;
756        }
757        let v0 = value.first()?;
758        let v1 = value.get(1)?;
759        let v2 = value.get(2)?;
760        let array = [v0.as_usize()?, v1.as_usize()?, v2.as_usize()?];
761        Some(array)
762    }
763}
764
765impl<'a> FromKclValue<'a> for [f64; 3] {
766    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
767        let KclValue::MixedArray { value, meta: _ } = arg else {
768            return None;
769        };
770        if value.len() != 3 {
771            return None;
772        }
773        let v0 = value.first()?;
774        let v1 = value.get(1)?;
775        let v2 = value.get(2)?;
776        let array = [v0.as_f64()?, v1.as_f64()?, v2.as_f64()?];
777        Some(array)
778    }
779}
780
781impl<'a> FromKclValue<'a> for TagNode {
782    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
783        arg.get_tag_declarator().ok()
784    }
785}
786
787impl<'a> FromKclValue<'a> for TagIdentifier {
788    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
789        arg.get_tag_identifier().ok()
790    }
791}
792
793impl<'a> FromKclValue<'a> for KclValue {
794    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
795        Some(arg.clone())
796    }
797}
798
799macro_rules! let_field_of {
800    // Optional field
801    ($obj:ident, $field:ident?) => {
802        let $field = $obj.get(stringify!($field)).and_then(FromKclValue::from_kcl_val);
803    };
804    // Optional field but with a different string used as the key
805    ($obj:ident, $field:ident? $key:literal) => {
806        let $field = $obj.get($key).and_then(FromKclValue::from_kcl_val);
807    };
808    // Mandatory field, but with a different string used as the key.
809    ($obj:ident, $field:ident $key:literal) => {
810        let $field = $obj.get($key).and_then(FromKclValue::from_kcl_val)?;
811    };
812    // Mandatory field, optionally with a type annotation
813    ($obj:ident, $field:ident $(, $annotation:ty)?) => {
814        let $field $(: $annotation)? = $obj.get(stringify!($field)).and_then(FromKclValue::from_kcl_val)?;
815    };
816}
817
818impl<'a> FromKclValue<'a> for crate::std::import::ImportFormat {
819    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
820        let obj = arg.as_object()?;
821        let_field_of!(obj, typ "format");
822        match typ {
823            "fbx" => Some(Self::Fbx {}),
824            "gltf" => Some(Self::Gltf {}),
825            "sldprt" => Some(Self::Sldprt {}),
826            "step" => Some(Self::Step {}),
827            "stl" => {
828                let_field_of!(obj, coords?);
829                let_field_of!(obj, units);
830                Some(Self::Stl { coords, units })
831            }
832            "obj" => {
833                let_field_of!(obj, coords?);
834                let_field_of!(obj, units);
835                Some(Self::Obj { coords, units })
836            }
837            "ply" => {
838                let_field_of!(obj, coords?);
839                let_field_of!(obj, units);
840                Some(Self::Ply { coords, units })
841            }
842            _ => None,
843        }
844    }
845}
846
847impl<'a> FromKclValue<'a> for super::sketch::AngledLineThatIntersectsData {
848    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
849        let obj = arg.as_object()?;
850        let_field_of!(obj, angle);
851        let_field_of!(obj, intersect_tag "intersectTag");
852        let_field_of!(obj, offset?);
853        Some(Self {
854            angle,
855            intersect_tag,
856            offset,
857        })
858    }
859}
860
861impl<'a> FromKclValue<'a> for super::shapes::PolygonData {
862    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
863        let obj = arg.as_object()?;
864        let_field_of!(obj, radius);
865        let_field_of!(obj, num_sides "numSides");
866        let_field_of!(obj, center);
867        let_field_of!(obj, inscribed);
868        let polygon_type = if inscribed {
869            PolygonType::Inscribed
870        } else {
871            PolygonType::Circumscribed
872        };
873        Some(Self {
874            radius,
875            num_sides,
876            center,
877            polygon_type,
878            inscribed,
879        })
880    }
881}
882
883impl<'a> FromKclValue<'a> for crate::std::polar::PolarCoordsData {
884    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
885        let obj = arg.as_object()?;
886        let_field_of!(obj, angle);
887        let_field_of!(obj, length);
888        Some(Self { angle, length })
889    }
890}
891
892impl<'a> FromKclValue<'a> for crate::execution::Plane {
893    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
894        arg.as_plane().cloned()
895    }
896}
897
898impl<'a> FromKclValue<'a> for crate::execution::PlaneType {
899    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
900        let plane_type = match arg.as_str()? {
901            "XY" | "xy" => Self::XY,
902            "XZ" | "xz" => Self::XZ,
903            "YZ" | "yz" => Self::YZ,
904            "Custom" => Self::Custom,
905            _ => return None,
906        };
907        Some(plane_type)
908    }
909}
910
911impl<'a> FromKclValue<'a> for kittycad_modeling_cmds::units::UnitLength {
912    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
913        let s = arg.as_str()?;
914        s.parse().ok()
915    }
916}
917
918impl<'a> FromKclValue<'a> for kittycad_modeling_cmds::coord::System {
919    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
920        let obj = arg.as_object()?;
921        let_field_of!(obj, forward);
922        let_field_of!(obj, up);
923        Some(Self { forward, up })
924    }
925}
926
927impl<'a> FromKclValue<'a> for kittycad_modeling_cmds::coord::AxisDirectionPair {
928    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
929        let obj = arg.as_object()?;
930        let_field_of!(obj, axis);
931        let_field_of!(obj, direction);
932        Some(Self { axis, direction })
933    }
934}
935
936impl<'a> FromKclValue<'a> for kittycad_modeling_cmds::coord::Axis {
937    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
938        let s = arg.as_str()?;
939        match s {
940            "y" => Some(Self::Y),
941            "z" => Some(Self::Z),
942            _ => None,
943        }
944    }
945}
946
947impl<'a> FromKclValue<'a> for PolygonType {
948    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
949        let s = arg.as_str()?;
950        match s {
951            "inscribed" => Some(Self::Inscribed),
952            _ => Some(Self::Circumscribed),
953        }
954    }
955}
956
957impl<'a> FromKclValue<'a> for kittycad_modeling_cmds::coord::Direction {
958    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
959        let s = arg.as_str()?;
960        match s {
961            "positive" => Some(Self::Positive),
962            "negative" => Some(Self::Negative),
963            _ => None,
964        }
965    }
966}
967
968impl<'a> FromKclValue<'a> for super::sketch::BezierData {
969    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
970        let obj = arg.as_object()?;
971        let_field_of!(obj, to);
972        let_field_of!(obj, control1);
973        let_field_of!(obj, control2);
974        Some(Self { to, control1, control2 })
975    }
976}
977
978impl<'a> FromKclValue<'a> for super::helix::HelixRevolutionsData {
979    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
980        let obj = arg.as_object()?;
981        let_field_of!(obj, revolutions);
982        let_field_of!(obj, length?);
983        let_field_of!(obj, ccw?);
984        let ccw = ccw.unwrap_or_default();
985        let angle_start = obj.get("angleStart")?.as_f64()?;
986        Some(Self {
987            revolutions,
988            angle_start,
989            ccw,
990            length,
991        })
992    }
993}
994
995impl<'a> FromKclValue<'a> for FaceTag {
996    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
997        let case1 = || match arg.as_str() {
998            Some("start" | "START") => Some(Self::StartOrEnd(super::sketch::StartOrEnd::Start)),
999            Some("end" | "END") => Some(Self::StartOrEnd(super::sketch::StartOrEnd::End)),
1000            _ => None,
1001        };
1002        let case2 = || {
1003            let tag = TagIdentifier::from_kcl_val(arg)?;
1004            Some(Self::Tag(Box::new(tag)))
1005        };
1006        case1().or_else(case2)
1007    }
1008}
1009
1010impl<'a> FromKclValue<'a> for super::sketch::AngledLineToData {
1011    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1012        // Deserialize from an {angle, to} object.
1013        let case1 = || {
1014            let obj = arg.as_object()?;
1015            let_field_of!(obj, to);
1016            let_field_of!(obj, angle);
1017            Some(Self { angle, to })
1018        };
1019        // Deserialize from an [angle, to] array.
1020        let case2 = || {
1021            let [angle, to] = arg.as_point2d()?;
1022            Some(Self { angle, to })
1023        };
1024        case1().or_else(case2)
1025    }
1026}
1027
1028impl<'a> FromKclValue<'a> for super::sketch::ArcData {
1029    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1030        let obj = arg.as_object()?;
1031        let_field_of!(obj, radius);
1032        let case1 = || {
1033            let angle_start = obj.get("angleStart")?.as_f64()?;
1034            let angle_end = obj.get("angleEnd")?.as_f64()?;
1035            Some(Self::AnglesAndRadius {
1036                angle_start,
1037                angle_end,
1038                radius,
1039            })
1040        };
1041        let case2 = || {
1042            let obj = arg.as_object()?;
1043            let_field_of!(obj, to);
1044            let_field_of!(obj, center);
1045            Some(Self::CenterToRadius { center, to, radius })
1046        };
1047        case1().or_else(case2)
1048    }
1049}
1050
1051impl<'a> FromKclValue<'a> for super::sketch::ArcToData {
1052    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1053        let obj = arg.as_object()?;
1054        let_field_of!(obj, end);
1055        let_field_of!(obj, interior);
1056        Some(Self { end, interior })
1057    }
1058}
1059
1060impl<'a> FromKclValue<'a> for super::revolve::RevolveData {
1061    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1062        let obj = arg.as_object()?;
1063        let angle = obj.get("angle").and_then(|x| x.as_f64());
1064        let tolerance = obj.get("tolerance").and_then(|x| x.as_f64());
1065        let_field_of!(obj, axis);
1066        Some(Self { angle, axis, tolerance })
1067    }
1068}
1069
1070impl<'a> FromKclValue<'a> for super::sketch::TangentialArcData {
1071    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1072        let obj = arg.as_object()?;
1073        let_field_of!(obj, radius);
1074        let_field_of!(obj, offset);
1075        Some(Self::RadiusAndOffset { radius, offset })
1076    }
1077}
1078
1079impl<'a> FromKclValue<'a> for crate::execution::Point3d {
1080    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1081        // Case 1: object with x/y/z fields
1082        if let Some(obj) = arg.as_object() {
1083            let_field_of!(obj, x);
1084            let_field_of!(obj, y);
1085            let_field_of!(obj, z);
1086            return Some(Self { x, y, z });
1087        }
1088        // Case 2: Array of 3 numbers.
1089        let [x, y, z]: [f64; 3] = FromKclValue::from_kcl_val(arg)?;
1090        Some(Self { x, y, z })
1091    }
1092}
1093
1094impl<'a> FromKclValue<'a> for super::sketch::PlaneData {
1095    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1096        // Case 0: actual plane
1097        if let KclValue::Plane { value } = arg {
1098            return Some(Self::Plane {
1099                origin: value.origin,
1100                x_axis: value.x_axis,
1101                y_axis: value.y_axis,
1102                z_axis: value.z_axis,
1103            });
1104        }
1105        // Case 1: predefined plane
1106        if let Some(s) = arg.as_str() {
1107            return match s {
1108                "XY" | "xy" => Some(Self::XY),
1109                "-XY" | "-xy" => Some(Self::NegXY),
1110                "XZ" | "xz" => Some(Self::XZ),
1111                "-XZ" | "-xz" => Some(Self::NegXZ),
1112                "YZ" | "yz" => Some(Self::YZ),
1113                "-YZ" | "-yz" => Some(Self::NegYZ),
1114                _ => None,
1115            };
1116        }
1117        // Case 2: custom plane
1118        let obj = arg.as_object()?;
1119        let_field_of!(obj, plane, &KclObjectFields);
1120        let origin = plane.get("origin").and_then(FromKclValue::from_kcl_val)?;
1121        let x_axis = plane.get("xAxis").and_then(FromKclValue::from_kcl_val)?;
1122        let y_axis = plane.get("yAxis").and_then(FromKclValue::from_kcl_val)?;
1123        let z_axis = plane.get("zAxis").and_then(FromKclValue::from_kcl_val)?;
1124        Some(Self::Plane {
1125            origin,
1126            x_axis,
1127            y_axis,
1128            z_axis,
1129        })
1130    }
1131}
1132
1133impl<'a> FromKclValue<'a> for crate::execution::ExtrudePlane {
1134    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1135        let obj = arg.as_object()?;
1136        let_field_of!(obj, face_id "faceId");
1137        let tag = FromKclValue::from_kcl_val(obj.get("tag")?);
1138        let_field_of!(obj, geo_meta "geoMeta");
1139        Some(Self { face_id, tag, geo_meta })
1140    }
1141}
1142
1143impl<'a> FromKclValue<'a> for crate::execution::ExtrudeArc {
1144    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1145        let obj = arg.as_object()?;
1146        let_field_of!(obj, face_id "faceId");
1147        let tag = FromKclValue::from_kcl_val(obj.get("tag")?);
1148        let_field_of!(obj, geo_meta "geoMeta");
1149        Some(Self { face_id, tag, geo_meta })
1150    }
1151}
1152
1153impl<'a> FromKclValue<'a> for crate::execution::GeoMeta {
1154    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1155        let obj = arg.as_object()?;
1156        let_field_of!(obj, id);
1157        let_field_of!(obj, source_range "sourceRange");
1158        Some(Self {
1159            id,
1160            metadata: Metadata { source_range },
1161        })
1162    }
1163}
1164
1165impl<'a> FromKclValue<'a> for crate::execution::ChamferSurface {
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::FilletSurface {
1176    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1177        let obj = arg.as_object()?;
1178        let_field_of!(obj, face_id "faceId");
1179        let tag = FromKclValue::from_kcl_val(obj.get("tag")?);
1180        let_field_of!(obj, geo_meta "geoMeta");
1181        Some(Self { face_id, tag, geo_meta })
1182    }
1183}
1184
1185impl<'a> FromKclValue<'a> for ExtrudeSurface {
1186    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1187        let case1 = crate::execution::ExtrudePlane::from_kcl_val;
1188        let case2 = crate::execution::ExtrudeArc::from_kcl_val;
1189        let case3 = crate::execution::ChamferSurface::from_kcl_val;
1190        let case4 = crate::execution::FilletSurface::from_kcl_val;
1191        case1(arg)
1192            .map(Self::ExtrudePlane)
1193            .or_else(|| case2(arg).map(Self::ExtrudeArc))
1194            .or_else(|| case3(arg).map(Self::Chamfer))
1195            .or_else(|| case4(arg).map(Self::Fillet))
1196    }
1197}
1198
1199impl<'a> FromKclValue<'a> for crate::execution::EdgeCut {
1200    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1201        let obj = arg.as_object()?;
1202        let_field_of!(obj, typ "type");
1203        let tag = Box::new(obj.get("tag").and_then(FromKclValue::from_kcl_val));
1204        let_field_of!(obj, edge_id "edgeId");
1205        let_field_of!(obj, id);
1206        match typ {
1207            "fillet" => {
1208                let_field_of!(obj, radius);
1209                Some(Self::Fillet {
1210                    edge_id,
1211                    tag,
1212                    id,
1213                    radius,
1214                })
1215            }
1216            "chamfer" => {
1217                let_field_of!(obj, length);
1218                Some(Self::Chamfer {
1219                    id,
1220                    length,
1221                    edge_id,
1222                    tag,
1223                })
1224            }
1225            _ => None,
1226        }
1227    }
1228}
1229
1230macro_rules! impl_from_kcl_for_vec {
1231    ($typ:path) => {
1232        impl<'a> FromKclValue<'a> for Vec<$typ> {
1233            fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1234                arg.as_array()?
1235                    .iter()
1236                    .map(|value| FromKclValue::from_kcl_val(value))
1237                    .collect::<Option<_>>()
1238            }
1239        }
1240    };
1241}
1242
1243impl_from_kcl_for_vec!(FaceTag);
1244impl_from_kcl_for_vec!(crate::execution::EdgeCut);
1245impl_from_kcl_for_vec!(crate::execution::Metadata);
1246impl_from_kcl_for_vec!(super::fillet::EdgeReference);
1247impl_from_kcl_for_vec!(ExtrudeSurface);
1248impl_from_kcl_for_vec!(Sketch);
1249
1250impl<'a> FromKclValue<'a> for SourceRange {
1251    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1252        let KclValue::MixedArray { value, meta: _ } = arg else {
1253            return None;
1254        };
1255        if value.len() != 3 {
1256            return None;
1257        }
1258        let v0 = value.first()?;
1259        let v1 = value.get(1)?;
1260        let v2 = value.get(2)?;
1261        Some(SourceRange::new(
1262            v0.as_usize()?,
1263            v1.as_usize()?,
1264            ModuleId::from_usize(v2.as_usize()?),
1265        ))
1266    }
1267}
1268
1269impl<'a> FromKclValue<'a> for crate::execution::Metadata {
1270    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1271        FromKclValue::from_kcl_val(arg).map(|sr| Self { source_range: sr })
1272    }
1273}
1274
1275impl<'a> FromKclValue<'a> for crate::execution::Solid {
1276    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1277        arg.as_solid().cloned()
1278    }
1279}
1280
1281impl<'a> FromKclValue<'a> for super::sketch::SketchData {
1282    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1283        // Order is critical since PlaneData is a subset of Plane.
1284        let case1 = crate::execution::Plane::from_kcl_val;
1285        let case2 = super::sketch::PlaneData::from_kcl_val;
1286        let case3 = crate::execution::Solid::from_kcl_val;
1287        case1(arg)
1288            .map(Box::new)
1289            .map(Self::Plane)
1290            .or_else(|| case2(arg).map(Self::PlaneOrientation))
1291            .or_else(|| case3(arg).map(Box::new).map(Self::Solid))
1292    }
1293}
1294
1295impl<'a> FromKclValue<'a> for super::axis_or_reference::AxisAndOrigin2d {
1296    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1297        // Case 1: predefined planes.
1298        if let Some(s) = arg.as_str() {
1299            return match s {
1300                "X" | "x" => Some(Self::X),
1301                "Y" | "y" => Some(Self::Y),
1302                "-X" | "-x" => Some(Self::NegX),
1303                "-Y" | "-y" => Some(Self::NegY),
1304                _ => None,
1305            };
1306        }
1307        // Case 2: custom planes.
1308        let obj = arg.as_object()?;
1309        let_field_of!(obj, custom, &KclObjectFields);
1310        let_field_of!(custom, origin);
1311        let_field_of!(custom, axis);
1312        Some(Self::Custom { axis, origin })
1313    }
1314}
1315
1316impl<'a> FromKclValue<'a> for super::axis_or_reference::AxisAndOrigin3d {
1317    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1318        // Case 1: predefined planes.
1319        if let Some(s) = arg.as_str() {
1320            return match s {
1321                "X" | "x" => Some(Self::X),
1322                "Y" | "y" => Some(Self::Y),
1323                "Z" | "z" => Some(Self::Z),
1324                "-X" | "-x" => Some(Self::NegX),
1325                "-Y" | "-y" => Some(Self::NegY),
1326                "-Z" | "-z" => Some(Self::NegZ),
1327                _ => None,
1328            };
1329        }
1330        // Case 2: custom planes.
1331        let obj = arg.as_object()?;
1332        let_field_of!(obj, custom, &KclObjectFields);
1333        let_field_of!(custom, origin);
1334        let_field_of!(custom, axis);
1335        Some(Self::Custom { axis, origin })
1336    }
1337}
1338
1339impl<'a> FromKclValue<'a> for super::fillet::EdgeReference {
1340    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1341        let id = arg.as_uuid().map(Self::Uuid);
1342        let tag = || TagIdentifier::from_kcl_val(arg).map(Box::new).map(Self::Tag);
1343        id.or_else(tag)
1344    }
1345}
1346
1347impl<'a> FromKclValue<'a> for super::axis_or_reference::Axis2dOrEdgeReference {
1348    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1349        let case1 = super::axis_or_reference::AxisAndOrigin2d::from_kcl_val;
1350        let case2 = super::fillet::EdgeReference::from_kcl_val;
1351        case1(arg).map(Self::Axis).or_else(|| case2(arg).map(Self::Edge))
1352    }
1353}
1354
1355impl<'a> FromKclValue<'a> for super::axis_or_reference::Axis3dOrEdgeReference {
1356    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1357        let case1 = super::axis_or_reference::AxisAndOrigin3d::from_kcl_val;
1358        let case2 = super::fillet::EdgeReference::from_kcl_val;
1359        case1(arg).map(Self::Axis).or_else(|| case2(arg).map(Self::Edge))
1360    }
1361}
1362
1363impl<'a> FromKclValue<'a> for super::mirror::Mirror2dData {
1364    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1365        let obj = arg.as_object()?;
1366        let_field_of!(obj, axis);
1367        Some(Self { axis })
1368    }
1369}
1370
1371impl<'a> FromKclValue<'a> for super::sketch::AngledLineData {
1372    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1373        let case1 = |arg: &KclValue| {
1374            let obj = arg.as_object()?;
1375            let_field_of!(obj, angle);
1376            let_field_of!(obj, length);
1377            Some(Self::AngleAndLengthNamed { angle, length })
1378        };
1379        let case2 = |arg: &KclValue| {
1380            let array = arg.as_array()?;
1381            let ang = array.first()?.as_f64()?;
1382            let len = array.get(1)?.as_f64()?;
1383            Some(Self::AngleAndLengthPair([ang, len]))
1384        };
1385        case1(arg).or_else(|| case2(arg))
1386    }
1387}
1388
1389impl<'a> FromKclValue<'a> for i64 {
1390    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1391        match arg {
1392            KclValue::Number { value, .. } => crate::try_f64_to_i64(*value),
1393            _ => None,
1394        }
1395    }
1396}
1397
1398impl<'a> FromKclValue<'a> for &'a str {
1399    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1400        let KclValue::String { value, meta: _ } = arg else {
1401            return None;
1402        };
1403        Some(value)
1404    }
1405}
1406
1407impl<'a> FromKclValue<'a> for &'a KclObjectFields {
1408    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1409        let KclValue::Object { value, meta: _ } = arg else {
1410            return None;
1411        };
1412        Some(value)
1413    }
1414}
1415
1416impl<'a> FromKclValue<'a> for uuid::Uuid {
1417    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1418        let KclValue::Uuid { value, meta: _ } = arg else {
1419            return None;
1420        };
1421        Some(*value)
1422    }
1423}
1424
1425impl<'a> FromKclValue<'a> for u32 {
1426    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1427        match arg {
1428            KclValue::Number { value, .. } => crate::try_f64_to_u32(*value),
1429            _ => None,
1430        }
1431    }
1432}
1433
1434impl<'a> FromKclValue<'a> for NonZeroU32 {
1435    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1436        u32::from_kcl_val(arg).and_then(|x| x.try_into().ok())
1437    }
1438}
1439
1440impl<'a> FromKclValue<'a> for u64 {
1441    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1442        match arg {
1443            KclValue::Number { value, .. } => crate::try_f64_to_u64(*value),
1444            _ => None,
1445        }
1446    }
1447}
1448impl<'a> FromKclValue<'a> for f64 {
1449    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1450        match arg {
1451            KclValue::Number { value, .. } => Some(*value),
1452            _ => None,
1453        }
1454    }
1455}
1456impl<'a> FromKclValue<'a> for TyF64 {
1457    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1458        match arg {
1459            KclValue::Number { value, ty, .. } => Some(TyF64::new(*value, ty.clone())),
1460            _ => None,
1461        }
1462    }
1463}
1464impl<'a> FromKclValue<'a> for Sketch {
1465    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1466        let KclValue::Sketch { value } = arg else {
1467            return None;
1468        };
1469        Some(value.as_ref().to_owned())
1470    }
1471}
1472
1473impl<'a> FromKclValue<'a> for Helix {
1474    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1475        let KclValue::Helix { value } = arg else {
1476            return None;
1477        };
1478        Some(value.as_ref().to_owned())
1479    }
1480}
1481impl<'a> FromKclValue<'a> for SweepPath {
1482    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1483        let case1 = Sketch::from_kcl_val;
1484        let case2 = Helix::from_kcl_val;
1485        case1(arg)
1486            .map(Self::Sketch)
1487            .or_else(|| case2(arg).map(|arg0: Helix| Self::Helix(Box::new(arg0))))
1488    }
1489}
1490impl<'a> FromKclValue<'a> for String {
1491    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1492        let KclValue::String { value, meta: _ } = arg else {
1493            return None;
1494        };
1495        Some(value.to_owned())
1496    }
1497}
1498impl<'a> FromKclValue<'a> for crate::parsing::ast::types::KclNone {
1499    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1500        let KclValue::KclNone { value, meta: _ } = arg else {
1501            return None;
1502        };
1503        Some(value.to_owned())
1504    }
1505}
1506impl<'a> FromKclValue<'a> for bool {
1507    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1508        let KclValue::Bool { value, meta: _ } = arg else {
1509            return None;
1510        };
1511        Some(*value)
1512    }
1513}
1514
1515impl<'a> FromKclValue<'a> for SketchSet {
1516    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1517        match arg {
1518            KclValue::Sketch { value: sketch } => Some(SketchSet::from(sketch.to_owned())),
1519            KclValue::Sketches { value } => Some(SketchSet::from(value.to_owned())),
1520            KclValue::MixedArray { .. } => {
1521                let v: Option<Vec<Sketch>> = FromKclValue::from_kcl_val(arg);
1522                Some(SketchSet::Sketches(v?.iter().cloned().map(Box::new).collect()))
1523            }
1524            _ => None,
1525        }
1526    }
1527}
1528
1529impl<'a> FromKclValue<'a> for Box<Solid> {
1530    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1531        let KclValue::Solid { value } = arg else {
1532            return None;
1533        };
1534        Some(value.to_owned())
1535    }
1536}
1537
1538impl<'a> FromKclValue<'a> for &'a FunctionSource {
1539    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1540        arg.get_function()
1541    }
1542}
1543
1544impl<'a> FromKclValue<'a> for SolidSet {
1545    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1546        arg.get_solid_set().ok()
1547    }
1548}
1549
1550impl<'a> FromKclValue<'a> for SketchOrSurface {
1551    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1552        match arg {
1553            KclValue::Sketch { value: sg } => Some(Self::Sketch(sg.to_owned())),
1554            KclValue::Plane { value } => Some(Self::SketchSurface(SketchSurface::Plane(value.clone()))),
1555            KclValue::Face { value } => Some(Self::SketchSurface(SketchSurface::Face(value.clone()))),
1556            _ => None,
1557        }
1558    }
1559}
1560impl<'a> FromKclValue<'a> for SketchSurface {
1561    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1562        match arg {
1563            KclValue::Plane { value } => Some(Self::Plane(value.clone())),
1564            KclValue::Face { value } => Some(Self::Face(value.clone())),
1565            _ => None,
1566        }
1567    }
1568}
1569
1570impl From<Args> for Metadata {
1571    fn from(value: Args) -> Self {
1572        Self {
1573            source_range: value.source_range,
1574        }
1575    }
1576}
1577
1578impl From<Args> for Vec<Metadata> {
1579    fn from(value: Args) -> Self {
1580        vec![Metadata {
1581            source_range: value.source_range,
1582        }]
1583    }
1584}