kcl_lib/std/
args.rs

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