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