kcl_lib/std/
args.rs

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