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