1use std::num::NonZeroU32;
2
3use anyhow::Result;
4use serde::Serialize;
5
6use super::fillet::EdgeReference;
7pub use crate::execution::fn_call::Args;
8use crate::{
9 ModuleId, SourceRange,
10 errors::{KclError, KclErrorDetails},
11 execution::{
12 ExecState, ExtrudeSurface, Helix, KclObjectFields, KclValue, Metadata, PlaneInfo, Sketch, SketchSurface, Solid,
13 TagIdentifier,
14 kcl_value::FunctionSource,
15 types::{NumericType, PrimitiveType, RuntimeType, UnitAngle, UnitLen, UnitType},
16 },
17 parsing::ast::types::TagNode,
18 std::{
19 shapes::{PolygonType, SketchOrSurface},
20 sketch::FaceTag,
21 sweep::SweepPath,
22 },
23};
24
25const ERROR_STRING_SKETCH_TO_SOLID_HELPER: &str =
26 "You can convert a sketch (2D) into a Solid (3D) by calling a function like `extrude` or `revolve`";
27
28#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
29#[ts(export)]
30#[serde(rename_all = "camelCase")]
31pub struct TyF64 {
32 pub n: f64,
33 pub ty: NumericType,
34}
35
36impl TyF64 {
37 pub fn new(n: f64, ty: NumericType) -> Self {
38 Self { n, ty }
39 }
40
41 pub fn to_mm(&self) -> f64 {
42 self.to_length_units(UnitLen::Mm)
43 }
44
45 pub fn to_length_units(&self, units: UnitLen) -> f64 {
46 let len = match &self.ty {
47 NumericType::Default { len, .. } => *len,
48 NumericType::Known(UnitType::Length(len)) => *len,
49 t => unreachable!("expected length, found {t:?}"),
50 };
51
52 debug_assert_ne!(len, UnitLen::Unknown);
53
54 len.adjust_to(self.n, units).0
55 }
56
57 pub fn to_degrees(&self) -> f64 {
58 let angle = match self.ty {
59 NumericType::Default { angle, .. } => angle,
60 NumericType::Known(UnitType::Angle(angle)) => angle,
61 _ => unreachable!(),
62 };
63
64 debug_assert_ne!(angle, UnitAngle::Unknown);
65
66 angle.adjust_to(self.n, UnitAngle::Degrees).0
67 }
68
69 pub fn to_radians(&self) -> f64 {
70 let angle = match self.ty {
71 NumericType::Default { angle, .. } => angle,
72 NumericType::Known(UnitType::Angle(angle)) => angle,
73 _ => unreachable!(),
74 };
75
76 debug_assert_ne!(angle, UnitAngle::Unknown);
77
78 angle.adjust_to(self.n, UnitAngle::Radians).0
79 }
80 pub fn count(n: f64) -> Self {
81 Self {
82 n,
83 ty: NumericType::count(),
84 }
85 }
86
87 pub fn map_value(mut self, n: f64) -> Self {
88 self.n = n;
89 self
90 }
91}
92
93impl Args {
94 pub(crate) fn get_kw_arg_opt<T>(
95 &self,
96 label: &str,
97 ty: &RuntimeType,
98 exec_state: &mut ExecState,
99 ) -> Result<Option<T>, KclError>
100 where
101 T: for<'a> FromKclValue<'a>,
102 {
103 match self.labeled.get(label) {
104 None => return Ok(None),
105 Some(a) => {
106 if let KclValue::KclNone { .. } = &a.value {
107 return Ok(None);
108 }
109 }
110 }
111
112 self.get_kw_arg(label, ty, exec_state).map(Some)
113 }
114
115 pub(crate) fn get_kw_arg<T>(&self, label: &str, ty: &RuntimeType, exec_state: &mut ExecState) -> Result<T, KclError>
116 where
117 T: for<'a> FromKclValue<'a>,
118 {
119 let Some(arg) = self.labeled.get(label) else {
120 return Err(KclError::new_semantic(KclErrorDetails::new(
121 format!("This function requires a keyword argument `{label}`"),
122 vec![self.source_range],
123 )));
124 };
125
126 let arg = arg.value.coerce(ty, true, exec_state).map_err(|_| {
127 let actual_type = arg.value.principal_type();
128 let actual_type_name = actual_type
129 .as_ref()
130 .map(|t| t.to_string())
131 .unwrap_or_else(|| arg.value.human_friendly_type().to_owned());
132 let msg_base = format!(
133 "This function expected its `{label}` argument to be {} but it's actually of type {actual_type_name}",
134 ty.human_friendly_type(),
135 );
136 let suggestion = match (ty, actual_type) {
137 (RuntimeType::Primitive(PrimitiveType::Solid), Some(RuntimeType::Primitive(PrimitiveType::Sketch))) => {
138 Some(ERROR_STRING_SKETCH_TO_SOLID_HELPER)
139 }
140 (RuntimeType::Array(t, _), Some(RuntimeType::Primitive(PrimitiveType::Sketch)))
141 if **t == RuntimeType::Primitive(PrimitiveType::Solid) =>
142 {
143 Some(ERROR_STRING_SKETCH_TO_SOLID_HELPER)
144 }
145 _ => None,
146 };
147 let mut message = match suggestion {
148 None => msg_base,
149 Some(sugg) => format!("{msg_base}. {sugg}"),
150 };
151 if message.contains("one or more Solids or ImportedGeometry but it's actually of type Sketch") {
152 message = format!("{message}. {ERROR_STRING_SKETCH_TO_SOLID_HELPER}");
153 }
154 KclError::new_semantic(KclErrorDetails::new(message, arg.source_ranges()))
155 })?;
156
157 T::from_kcl_val(&arg).ok_or_else(|| {
158 KclError::new_internal(KclErrorDetails::new(
159 format!("Mismatch between type coercion and value extraction (this isn't your fault).\nTo assist in bug-reporting, expected type: {ty:?}; actual value: {arg:?}"),
160 vec![self.source_range],
161 ))
162 })
163 }
164
165 pub(crate) fn kw_arg_edge_array_and_source(
168 &self,
169 label: &str,
170 ) -> Result<Vec<(EdgeReference, SourceRange)>, KclError> {
171 let Some(arg) = self.labeled.get(label) else {
172 let err = KclError::new_semantic(KclErrorDetails::new(
173 format!("This function requires a keyword argument '{label}'"),
174 vec![self.source_range],
175 ));
176 return Err(err);
177 };
178 arg.value
179 .clone()
180 .into_array()
181 .iter()
182 .map(|item| {
183 let source = SourceRange::from(item);
184 let val = FromKclValue::from_kcl_val(item).ok_or_else(|| {
185 KclError::new_semantic(KclErrorDetails::new(
186 format!("Expected an Edge but found {}", arg.value.human_friendly_type()),
187 arg.source_ranges(),
188 ))
189 })?;
190 Ok((val, source))
191 })
192 .collect::<Result<Vec<_>, _>>()
193 }
194
195 pub(crate) fn get_unlabeled_kw_arg_array_and_type(
196 &self,
197 label: &str,
198 exec_state: &mut ExecState,
199 ) -> Result<(Vec<KclValue>, RuntimeType), KclError> {
200 let value = self.get_unlabeled_kw_arg(label, &RuntimeType::any_array(), exec_state)?;
201 Ok(match value {
202 KclValue::HomArray { value, ty } => (value, ty),
203 KclValue::Tuple { value, .. } => (value, RuntimeType::any()),
204 val => (vec![val], RuntimeType::any()),
205 })
206 }
207
208 pub(crate) fn get_unlabeled_kw_arg<T>(
211 &self,
212 label: &str,
213 ty: &RuntimeType,
214 exec_state: &mut ExecState,
215 ) -> Result<T, KclError>
216 where
217 T: for<'a> FromKclValue<'a>,
218 {
219 let arg = self
220 .unlabeled_kw_arg_unconverted()
221 .ok_or(KclError::new_semantic(KclErrorDetails::new(
222 format!("This function requires a value for the special unlabeled first parameter, '{label}'"),
223 vec![self.source_range],
224 )))?;
225
226 let arg = arg.value.coerce(ty, true, exec_state).map_err(|_| {
227 let actual_type = arg.value.principal_type();
228 let actual_type_name = actual_type
229 .as_ref()
230 .map(|t| t.to_string())
231 .unwrap_or_else(|| arg.value.human_friendly_type().to_owned());
232 let msg_base = format!(
233 "This function expected the input argument to be {} but it's actually of type {actual_type_name}",
234 ty.human_friendly_type(),
235 );
236 let suggestion = match (ty, actual_type) {
237 (RuntimeType::Primitive(PrimitiveType::Solid), Some(RuntimeType::Primitive(PrimitiveType::Sketch))) => {
238 Some(ERROR_STRING_SKETCH_TO_SOLID_HELPER)
239 }
240 (RuntimeType::Array(ty, _), Some(RuntimeType::Primitive(PrimitiveType::Sketch)))
241 if **ty == RuntimeType::Primitive(PrimitiveType::Solid) =>
242 {
243 Some(ERROR_STRING_SKETCH_TO_SOLID_HELPER)
244 }
245 _ => None,
246 };
247 let mut message = match suggestion {
248 None => msg_base,
249 Some(sugg) => format!("{msg_base}. {sugg}"),
250 };
251
252 if message.contains("one or more Solids or ImportedGeometry but it's actually of type Sketch") {
253 message = format!("{message}. {ERROR_STRING_SKETCH_TO_SOLID_HELPER}");
254 }
255 KclError::new_semantic(KclErrorDetails::new(message, arg.source_ranges()))
256 })?;
257
258 T::from_kcl_val(&arg).ok_or_else(|| {
259 KclError::new_internal(KclErrorDetails::new(
260 format!("Mismatch between type coercion and value extraction (this isn't your fault).\nTo assist in bug-reporting, expected type: {ty:?}; actual value: {arg:?}"),
261 vec![self.source_range],
262 ))
263 })
264 }
265
266 fn get_tag_info_from_memory<'a, 'e>(
268 &'a self,
269 exec_state: &'e mut ExecState,
270 tag: &'a TagIdentifier,
271 ) -> Result<&'e crate::execution::TagEngineInfo, KclError> {
272 if let (epoch, KclValue::TagIdentifier(t)) =
273 exec_state.stack().get_from_call_stack(&tag.value, self.source_range)?
274 {
275 let info = t.get_info(epoch).ok_or_else(|| {
276 KclError::new_type(KclErrorDetails::new(
277 format!("Tag `{}` does not have engine info", tag.value),
278 vec![self.source_range],
279 ))
280 })?;
281 Ok(info)
282 } else {
283 Err(KclError::new_type(KclErrorDetails::new(
284 format!("Tag `{}` does not exist", tag.value),
285 vec![self.source_range],
286 )))
287 }
288 }
289
290 pub(crate) fn get_tag_engine_info<'a, 'e>(
292 &'a self,
293 exec_state: &'e mut ExecState,
294 tag: &'a TagIdentifier,
295 ) -> Result<&'a crate::execution::TagEngineInfo, KclError>
296 where
297 'e: 'a,
298 {
299 if let Some(info) = tag.get_cur_info() {
300 return Ok(info);
301 }
302
303 self.get_tag_info_from_memory(exec_state, tag)
304 }
305
306 fn get_tag_engine_info_check_surface<'a, 'e>(
308 &'a self,
309 exec_state: &'e mut ExecState,
310 tag: &'a TagIdentifier,
311 ) -> Result<&'a crate::execution::TagEngineInfo, KclError>
312 where
313 'e: 'a,
314 {
315 if let Some(info) = tag.get_cur_info()
316 && info.surface.is_some()
317 {
318 return Ok(info);
319 }
320
321 self.get_tag_info_from_memory(exec_state, tag)
322 }
323
324 pub(crate) fn make_kcl_val_from_point(&self, p: [f64; 2], ty: NumericType) -> Result<KclValue, KclError> {
325 let meta = Metadata {
326 source_range: self.source_range,
327 };
328 let x = KclValue::Number {
329 value: p[0],
330 meta: vec![meta],
331 ty,
332 };
333 let y = KclValue::Number {
334 value: p[1],
335 meta: vec![meta],
336 ty,
337 };
338 let ty = RuntimeType::Primitive(PrimitiveType::Number(ty));
339
340 Ok(KclValue::HomArray { value: vec![x, y], ty })
341 }
342
343 pub(super) fn make_user_val_from_f64_with_type(&self, f: TyF64) -> KclValue {
344 KclValue::from_number_with_type(
345 f.n,
346 f.ty,
347 vec![Metadata {
348 source_range: self.source_range,
349 }],
350 )
351 }
352
353 pub(crate) async fn get_adjacent_face_to_tag(
355 &self,
356 exec_state: &mut ExecState,
357 tag: &TagIdentifier,
358 must_be_planar: bool,
359 ) -> Result<uuid::Uuid, KclError> {
360 if tag.value.is_empty() {
361 return Err(KclError::new_type(KclErrorDetails::new(
362 "Expected a non-empty tag for the face".to_string(),
363 vec![self.source_range],
364 )));
365 }
366
367 let engine_info = self.get_tag_engine_info_check_surface(exec_state, tag)?;
368
369 let surface = engine_info.surface.as_ref().ok_or_else(|| {
370 KclError::new_type(KclErrorDetails::new(
371 format!("Tag `{}` does not have a surface", tag.value),
372 vec![self.source_range],
373 ))
374 })?;
375
376 if let Some(face_from_surface) = match surface {
377 ExtrudeSurface::ExtrudePlane(extrude_plane) => {
378 if let Some(plane_tag) = &extrude_plane.tag {
379 if plane_tag.name == tag.value {
380 Some(Ok(extrude_plane.face_id))
381 } else {
382 None
383 }
384 } else {
385 None
386 }
387 }
388 ExtrudeSurface::ExtrudeArc(_) if must_be_planar => Some(Err(KclError::new_type(KclErrorDetails::new(
390 format!("Tag `{}` is a non-planar surface", tag.value),
391 vec![self.source_range],
392 )))),
393 ExtrudeSurface::ExtrudeArc(extrude_arc) => {
394 if let Some(arc_tag) = &extrude_arc.tag {
395 if arc_tag.name == tag.value {
396 Some(Ok(extrude_arc.face_id))
397 } else {
398 None
399 }
400 } else {
401 None
402 }
403 }
404 ExtrudeSurface::Chamfer(chamfer) => {
405 if let Some(chamfer_tag) = &chamfer.tag {
406 if chamfer_tag.name == tag.value {
407 Some(Ok(chamfer.face_id))
408 } else {
409 None
410 }
411 } else {
412 None
413 }
414 }
415 ExtrudeSurface::Fillet(_) if must_be_planar => Some(Err(KclError::new_type(KclErrorDetails::new(
417 format!("Tag `{}` is a non-planar surface", tag.value),
418 vec![self.source_range],
419 )))),
420 ExtrudeSurface::Fillet(fillet) => {
421 if let Some(fillet_tag) = &fillet.tag {
422 if fillet_tag.name == tag.value {
423 Some(Ok(fillet.face_id))
424 } else {
425 None
426 }
427 } else {
428 None
429 }
430 }
431 } {
432 return face_from_surface;
433 }
434
435 Err(KclError::new_type(KclErrorDetails::new(
437 format!("Expected a face with the tag `{}`", tag.value),
438 vec![self.source_range],
439 )))
440 }
441}
442
443pub trait FromKclValue<'a>: Sized {
445 fn from_kcl_val(arg: &'a KclValue) -> Option<Self>;
447}
448
449impl<'a> FromKclValue<'a> for TagNode {
450 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
451 arg.get_tag_declarator().ok()
452 }
453}
454
455impl<'a> FromKclValue<'a> for TagIdentifier {
456 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
457 arg.get_tag_identifier().ok()
458 }
459}
460
461impl<'a> FromKclValue<'a> for Vec<TagIdentifier> {
462 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
463 match arg {
464 KclValue::HomArray { value, .. } => {
465 let tags = value.iter().map(|v| v.get_tag_identifier().unwrap()).collect();
466 Some(tags)
467 }
468 KclValue::Tuple { value, .. } => {
469 let tags = value.iter().map(|v| v.get_tag_identifier().unwrap()).collect();
470 Some(tags)
471 }
472 _ => None,
473 }
474 }
475}
476
477impl<'a> FromKclValue<'a> for Vec<KclValue> {
478 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
479 Some(arg.clone().into_array())
480 }
481}
482
483impl<'a> FromKclValue<'a> for KclValue {
484 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
485 Some(arg.clone())
486 }
487}
488
489macro_rules! let_field_of {
490 ($obj:ident, $field:ident?) => {
492 let $field = $obj.get(stringify!($field)).and_then(FromKclValue::from_kcl_val);
493 };
494 ($obj:ident, $field:ident? $key:literal) => {
496 let $field = $obj.get($key).and_then(FromKclValue::from_kcl_val);
497 };
498 ($obj:ident, $field:ident $key:literal) => {
500 let $field = $obj.get($key).and_then(FromKclValue::from_kcl_val)?;
501 };
502 ($obj:ident, $field:ident $(, $annotation:ty)?) => {
504 let $field $(: $annotation)? = $obj.get(stringify!($field)).and_then(FromKclValue::from_kcl_val)?;
505 };
506}
507
508impl<'a> FromKclValue<'a> for crate::execution::Plane {
509 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
510 arg.as_plane().cloned()
511 }
512}
513
514impl<'a> FromKclValue<'a> for crate::execution::PlaneType {
515 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
516 let plane_type = match arg.as_str()? {
517 "XY" | "xy" => Self::XY,
518 "XZ" | "xz" => Self::XZ,
519 "YZ" | "yz" => Self::YZ,
520 "Custom" => Self::Custom,
521 _ => return None,
522 };
523 Some(plane_type)
524 }
525}
526
527impl<'a> FromKclValue<'a> for kittycad_modeling_cmds::units::UnitLength {
528 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
529 let s = arg.as_str()?;
530 s.parse().ok()
531 }
532}
533
534impl<'a> FromKclValue<'a> for kittycad_modeling_cmds::coord::System {
535 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
536 let obj = arg.as_object()?;
537 let_field_of!(obj, forward);
538 let_field_of!(obj, up);
539 Some(Self { forward, up })
540 }
541}
542
543impl<'a> FromKclValue<'a> for kittycad_modeling_cmds::coord::AxisDirectionPair {
544 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
545 let obj = arg.as_object()?;
546 let_field_of!(obj, axis);
547 let_field_of!(obj, direction);
548 Some(Self { axis, direction })
549 }
550}
551
552impl<'a> FromKclValue<'a> for kittycad_modeling_cmds::coord::Axis {
553 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
554 let s = arg.as_str()?;
555 match s {
556 "y" => Some(Self::Y),
557 "z" => Some(Self::Z),
558 _ => None,
559 }
560 }
561}
562
563impl<'a> FromKclValue<'a> for PolygonType {
564 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
565 let s = arg.as_str()?;
566 match s {
567 "inscribed" => Some(Self::Inscribed),
568 _ => Some(Self::Circumscribed),
569 }
570 }
571}
572
573impl<'a> FromKclValue<'a> for kittycad_modeling_cmds::coord::Direction {
574 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
575 let s = arg.as_str()?;
576 match s {
577 "positive" => Some(Self::Positive),
578 "negative" => Some(Self::Negative),
579 _ => None,
580 }
581 }
582}
583
584impl<'a> FromKclValue<'a> for crate::execution::Geometry {
585 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
586 match arg {
587 KclValue::Sketch { value } => Some(Self::Sketch(*value.to_owned())),
588 KclValue::Solid { value } => Some(Self::Solid(*value.to_owned())),
589 _ => None,
590 }
591 }
592}
593
594impl<'a> FromKclValue<'a> for crate::execution::GeometryWithImportedGeometry {
595 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
596 match arg {
597 KclValue::Sketch { value } => Some(Self::Sketch(*value.to_owned())),
598 KclValue::Solid { value } => Some(Self::Solid(*value.to_owned())),
599 KclValue::ImportedGeometry(value) => Some(Self::ImportedGeometry(Box::new(value.clone()))),
600 _ => None,
601 }
602 }
603}
604
605impl<'a> FromKclValue<'a> for FaceTag {
606 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
607 let case1 = || match arg.as_str() {
608 Some("start" | "START") => Some(Self::StartOrEnd(super::sketch::StartOrEnd::Start)),
609 Some("end" | "END") => Some(Self::StartOrEnd(super::sketch::StartOrEnd::End)),
610 _ => None,
611 };
612 let case2 = || {
613 let tag = TagIdentifier::from_kcl_val(arg)?;
614 Some(Self::Tag(Box::new(tag)))
615 };
616 case1().or_else(case2)
617 }
618}
619
620impl<'a> FromKclValue<'a> for super::sketch::TangentialArcData {
621 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
622 let obj = arg.as_object()?;
623 let_field_of!(obj, radius);
624 let_field_of!(obj, offset);
625 Some(Self::RadiusAndOffset { radius, offset })
626 }
627}
628
629impl<'a> FromKclValue<'a> for crate::execution::Point3d {
630 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
631 if let Some(obj) = arg.as_object() {
633 let_field_of!(obj, x, TyF64);
634 let_field_of!(obj, y, TyF64);
635 let_field_of!(obj, z, TyF64);
636 let (a, ty) = NumericType::combine_eq_array(&[x, y, z]);
638 return Some(Self {
639 x: a[0],
640 y: a[1],
641 z: a[2],
642 units: ty.as_length().unwrap_or(UnitLen::Unknown),
643 });
644 }
645 let [x, y, z]: [TyF64; 3] = FromKclValue::from_kcl_val(arg)?;
647 let (a, ty) = NumericType::combine_eq_array(&[x, y, z]);
648 Some(Self {
649 x: a[0],
650 y: a[1],
651 z: a[2],
652 units: ty.as_length().unwrap_or(UnitLen::Unknown),
653 })
654 }
655}
656
657impl<'a> FromKclValue<'a> for super::sketch::PlaneData {
658 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
659 if let KclValue::Plane { value } = arg {
661 return Some(Self::Plane(PlaneInfo {
662 origin: value.info.origin,
663 x_axis: value.info.x_axis,
664 y_axis: value.info.y_axis,
665 z_axis: value.info.z_axis,
666 }));
667 }
668 if let Some(s) = arg.as_str() {
670 return match s {
671 "XY" | "xy" => Some(Self::XY),
672 "-XY" | "-xy" => Some(Self::NegXY),
673 "XZ" | "xz" => Some(Self::XZ),
674 "-XZ" | "-xz" => Some(Self::NegXZ),
675 "YZ" | "yz" => Some(Self::YZ),
676 "-YZ" | "-yz" => Some(Self::NegYZ),
677 _ => None,
678 };
679 }
680 let obj = arg.as_object()?;
682 let_field_of!(obj, plane, &KclObjectFields);
683 let origin = plane.get("origin").and_then(FromKclValue::from_kcl_val)?;
684 let x_axis: crate::execution::Point3d = plane.get("xAxis").and_then(FromKclValue::from_kcl_val)?;
685 let y_axis = plane.get("yAxis").and_then(FromKclValue::from_kcl_val)?;
686 let z_axis = x_axis.axes_cross_product(&y_axis);
687 Some(Self::Plane(PlaneInfo {
688 origin,
689 x_axis,
690 y_axis,
691 z_axis,
692 }))
693 }
694}
695
696impl<'a> FromKclValue<'a> for crate::execution::ExtrudePlane {
697 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
698 let obj = arg.as_object()?;
699 let_field_of!(obj, face_id "faceId");
700 let tag = FromKclValue::from_kcl_val(obj.get("tag")?);
701 let_field_of!(obj, geo_meta "geoMeta");
702 Some(Self { face_id, tag, geo_meta })
703 }
704}
705
706impl<'a> FromKclValue<'a> for crate::execution::ExtrudeArc {
707 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
708 let obj = arg.as_object()?;
709 let_field_of!(obj, face_id "faceId");
710 let tag = FromKclValue::from_kcl_val(obj.get("tag")?);
711 let_field_of!(obj, geo_meta "geoMeta");
712 Some(Self { face_id, tag, geo_meta })
713 }
714}
715
716impl<'a> FromKclValue<'a> for crate::execution::GeoMeta {
717 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
718 let obj = arg.as_object()?;
719 let_field_of!(obj, id);
720 let_field_of!(obj, source_range "sourceRange");
721 Some(Self {
722 id,
723 metadata: Metadata { source_range },
724 })
725 }
726}
727
728impl<'a> FromKclValue<'a> for crate::execution::ChamferSurface {
729 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
730 let obj = arg.as_object()?;
731 let_field_of!(obj, face_id "faceId");
732 let tag = FromKclValue::from_kcl_val(obj.get("tag")?);
733 let_field_of!(obj, geo_meta "geoMeta");
734 Some(Self { face_id, tag, geo_meta })
735 }
736}
737
738impl<'a> FromKclValue<'a> for crate::execution::FilletSurface {
739 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
740 let obj = arg.as_object()?;
741 let_field_of!(obj, face_id "faceId");
742 let tag = FromKclValue::from_kcl_val(obj.get("tag")?);
743 let_field_of!(obj, geo_meta "geoMeta");
744 Some(Self { face_id, tag, geo_meta })
745 }
746}
747
748impl<'a> FromKclValue<'a> for ExtrudeSurface {
749 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
750 let case1 = crate::execution::ExtrudePlane::from_kcl_val;
751 let case2 = crate::execution::ExtrudeArc::from_kcl_val;
752 let case3 = crate::execution::ChamferSurface::from_kcl_val;
753 let case4 = crate::execution::FilletSurface::from_kcl_val;
754 case1(arg)
755 .map(Self::ExtrudePlane)
756 .or_else(|| case2(arg).map(Self::ExtrudeArc))
757 .or_else(|| case3(arg).map(Self::Chamfer))
758 .or_else(|| case4(arg).map(Self::Fillet))
759 }
760}
761
762impl<'a> FromKclValue<'a> for crate::execution::EdgeCut {
763 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
764 let obj = arg.as_object()?;
765 let_field_of!(obj, typ "type");
766 let tag = Box::new(obj.get("tag").and_then(FromKclValue::from_kcl_val));
767 let_field_of!(obj, edge_id "edgeId");
768 let_field_of!(obj, id);
769 match typ {
770 "fillet" => {
771 let_field_of!(obj, radius);
772 Some(Self::Fillet {
773 edge_id,
774 tag,
775 id,
776 radius,
777 })
778 }
779 "chamfer" => {
780 let_field_of!(obj, length);
781 Some(Self::Chamfer {
782 id,
783 length,
784 edge_id,
785 tag,
786 })
787 }
788 _ => None,
789 }
790 }
791}
792
793macro_rules! impl_from_kcl_for_vec {
794 ($typ:path) => {
795 impl<'a> FromKclValue<'a> for Vec<$typ> {
796 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
797 arg.clone()
798 .into_array()
799 .iter()
800 .map(|value| FromKclValue::from_kcl_val(value))
801 .collect::<Option<_>>()
802 }
803 }
804 };
805}
806
807impl_from_kcl_for_vec!(FaceTag);
808impl_from_kcl_for_vec!(crate::execution::EdgeCut);
809impl_from_kcl_for_vec!(crate::execution::Metadata);
810impl_from_kcl_for_vec!(super::fillet::EdgeReference);
811impl_from_kcl_for_vec!(ExtrudeSurface);
812impl_from_kcl_for_vec!(TyF64);
813impl_from_kcl_for_vec!(Solid);
814impl_from_kcl_for_vec!(Sketch);
815
816impl<'a> FromKclValue<'a> for SourceRange {
817 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
818 let value = match arg {
819 KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } => value,
820 _ => {
821 return None;
822 }
823 };
824 if value.len() != 3 {
825 return None;
826 }
827 let v0 = value.first()?;
828 let v1 = value.get(1)?;
829 let v2 = value.get(2)?;
830 Some(SourceRange::new(
831 v0.as_usize()?,
832 v1.as_usize()?,
833 ModuleId::from_usize(v2.as_usize()?),
834 ))
835 }
836}
837
838impl<'a> FromKclValue<'a> for crate::execution::Metadata {
839 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
840 FromKclValue::from_kcl_val(arg).map(|sr| Self { source_range: sr })
841 }
842}
843
844impl<'a> FromKclValue<'a> for crate::execution::Solid {
845 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
846 arg.as_solid().cloned()
847 }
848}
849
850impl<'a> FromKclValue<'a> for crate::execution::SolidOrSketchOrImportedGeometry {
851 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
852 match arg {
853 KclValue::Solid { value } => Some(Self::SolidSet(vec![(**value).clone()])),
854 KclValue::Sketch { value } => Some(Self::SketchSet(vec![(**value).clone()])),
855 KclValue::HomArray { value, .. } => {
856 let mut solids = vec![];
857 let mut sketches = vec![];
858 for item in value {
859 match item {
860 KclValue::Solid { value } => solids.push((**value).clone()),
861 KclValue::Sketch { value } => sketches.push((**value).clone()),
862 _ => return None,
863 }
864 }
865 if !solids.is_empty() {
866 Some(Self::SolidSet(solids))
867 } else {
868 Some(Self::SketchSet(sketches))
869 }
870 }
871 KclValue::ImportedGeometry(value) => Some(Self::ImportedGeometry(Box::new(value.clone()))),
872 _ => None,
873 }
874 }
875}
876
877impl<'a> FromKclValue<'a> for crate::execution::SolidOrImportedGeometry {
878 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
879 match arg {
880 KclValue::Solid { value } => Some(Self::SolidSet(vec![(**value).clone()])),
881 KclValue::HomArray { value, .. } => {
882 let mut solids = vec![];
883 for item in value {
884 match item {
885 KclValue::Solid { value } => solids.push((**value).clone()),
886 _ => return None,
887 }
888 }
889 Some(Self::SolidSet(solids))
890 }
891 KclValue::ImportedGeometry(value) => Some(Self::ImportedGeometry(Box::new(value.clone()))),
892 _ => None,
893 }
894 }
895}
896
897impl<'a> FromKclValue<'a> for super::sketch::SketchData {
898 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
899 let case1 = crate::execution::Plane::from_kcl_val;
901 let case2 = super::sketch::PlaneData::from_kcl_val;
902 let case3 = crate::execution::Solid::from_kcl_val;
903 let case4 = <Vec<Solid>>::from_kcl_val;
904 case1(arg)
905 .map(Box::new)
906 .map(Self::Plane)
907 .or_else(|| case2(arg).map(Self::PlaneOrientation))
908 .or_else(|| case3(arg).map(Box::new).map(Self::Solid))
909 .or_else(|| case4(arg).map(|v| Box::new(v[0].clone())).map(Self::Solid))
910 }
911}
912
913impl<'a> FromKclValue<'a> for super::fillet::EdgeReference {
914 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
915 let id = arg.as_uuid().map(Self::Uuid);
916 let tag = || TagIdentifier::from_kcl_val(arg).map(Box::new).map(Self::Tag);
917 id.or_else(tag)
918 }
919}
920
921impl<'a> FromKclValue<'a> for super::axis_or_reference::Axis2dOrEdgeReference {
922 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
923 let case1 = |arg: &KclValue| {
924 let obj = arg.as_object()?;
925 let_field_of!(obj, direction);
926 let_field_of!(obj, origin);
927 Some(Self::Axis { direction, origin })
928 };
929 let case2 = super::fillet::EdgeReference::from_kcl_val;
930 case1(arg).or_else(|| case2(arg).map(Self::Edge))
931 }
932}
933
934impl<'a> FromKclValue<'a> for super::axis_or_reference::Axis3dOrEdgeReference {
935 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
936 let case1 = |arg: &KclValue| {
937 let obj = arg.as_object()?;
938 let_field_of!(obj, direction);
939 let_field_of!(obj, origin);
940 Some(Self::Axis { direction, origin })
941 };
942 let case2 = super::fillet::EdgeReference::from_kcl_val;
943 case1(arg).or_else(|| case2(arg).map(Self::Edge))
944 }
945}
946
947impl<'a> FromKclValue<'a> for super::axis_or_reference::Axis2dOrPoint2d {
948 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
949 let case1 = |arg: &KclValue| {
950 let obj = arg.as_object()?;
951 let_field_of!(obj, direction);
952 let_field_of!(obj, origin);
953 Some(Self::Axis { direction, origin })
954 };
955 let case2 = <[TyF64; 2]>::from_kcl_val;
956 case1(arg).or_else(|| case2(arg).map(Self::Point))
957 }
958}
959
960impl<'a> FromKclValue<'a> for super::axis_or_reference::Axis3dOrPoint3d {
961 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
962 let case1 = |arg: &KclValue| {
963 let obj = arg.as_object()?;
964 let_field_of!(obj, direction);
965 let_field_of!(obj, origin);
966 Some(Self::Axis { direction, origin })
967 };
968 let case2 = <[TyF64; 3]>::from_kcl_val;
969 case1(arg).or_else(|| case2(arg).map(Self::Point))
970 }
971}
972
973impl<'a> FromKclValue<'a> for i64 {
974 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
975 match arg {
976 KclValue::Number { value, .. } => crate::try_f64_to_i64(*value),
977 _ => None,
978 }
979 }
980}
981
982impl<'a> FromKclValue<'a> for &'a str {
983 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
984 let KclValue::String { value, meta: _ } = arg else {
985 return None;
986 };
987 Some(value)
988 }
989}
990
991impl<'a> FromKclValue<'a> for &'a KclObjectFields {
992 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
993 let KclValue::Object { value, meta: _ } = arg else {
994 return None;
995 };
996 Some(value)
997 }
998}
999
1000impl<'a> FromKclValue<'a> for uuid::Uuid {
1001 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1002 let KclValue::Uuid { value, meta: _ } = arg else {
1003 return None;
1004 };
1005 Some(*value)
1006 }
1007}
1008
1009impl<'a> FromKclValue<'a> for u32 {
1010 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1011 match arg {
1012 KclValue::Number { value, .. } => crate::try_f64_to_u32(*value),
1013 _ => None,
1014 }
1015 }
1016}
1017
1018impl<'a> FromKclValue<'a> for NonZeroU32 {
1019 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1020 u32::from_kcl_val(arg).and_then(|x| x.try_into().ok())
1021 }
1022}
1023
1024impl<'a> FromKclValue<'a> for u64 {
1025 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1026 match arg {
1027 KclValue::Number { value, .. } => crate::try_f64_to_u64(*value),
1028 _ => None,
1029 }
1030 }
1031}
1032
1033impl<'a> FromKclValue<'a> for TyF64 {
1034 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1035 match arg {
1036 KclValue::Number { value, ty, .. } => Some(TyF64::new(*value, *ty)),
1037 _ => None,
1038 }
1039 }
1040}
1041
1042impl<'a> FromKclValue<'a> for [TyF64; 2] {
1043 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1044 match arg {
1045 KclValue::Tuple { value, meta: _ } | KclValue::HomArray { value, .. } => {
1046 if value.len() != 2 {
1047 return None;
1048 }
1049 let v0 = value.first()?;
1050 let v1 = value.get(1)?;
1051 let array = [v0.as_ty_f64()?, v1.as_ty_f64()?];
1052 Some(array)
1053 }
1054 _ => None,
1055 }
1056 }
1057}
1058
1059impl<'a> FromKclValue<'a> for [TyF64; 3] {
1060 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1061 match arg {
1062 KclValue::Tuple { value, meta: _ } | KclValue::HomArray { value, .. } => {
1063 if value.len() != 3 {
1064 return None;
1065 }
1066 let v0 = value.first()?;
1067 let v1 = value.get(1)?;
1068 let v2 = value.get(2)?;
1069 let array = [v0.as_ty_f64()?, v1.as_ty_f64()?, v2.as_ty_f64()?];
1070 Some(array)
1071 }
1072 _ => None,
1073 }
1074 }
1075}
1076
1077impl<'a> FromKclValue<'a> for [TyF64; 6] {
1078 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1079 match arg {
1080 KclValue::Tuple { value, meta: _ } | KclValue::HomArray { value, .. } => {
1081 if value.len() != 6 {
1082 return None;
1083 }
1084 let v0 = value.first()?;
1085 let v1 = value.get(1)?;
1086 let v2 = value.get(2)?;
1087 let v3 = value.get(3)?;
1088 let v4 = value.get(4)?;
1089 let v5 = value.get(5)?;
1090 let array = [
1091 v0.as_ty_f64()?,
1092 v1.as_ty_f64()?,
1093 v2.as_ty_f64()?,
1094 v3.as_ty_f64()?,
1095 v4.as_ty_f64()?,
1096 v5.as_ty_f64()?,
1097 ];
1098 Some(array)
1099 }
1100 _ => None,
1101 }
1102 }
1103}
1104
1105impl<'a> FromKclValue<'a> for Sketch {
1106 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1107 let KclValue::Sketch { value } = arg else {
1108 return None;
1109 };
1110 Some(value.as_ref().to_owned())
1111 }
1112}
1113
1114impl<'a> FromKclValue<'a> for Helix {
1115 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1116 let KclValue::Helix { value } = arg else {
1117 return None;
1118 };
1119 Some(value.as_ref().to_owned())
1120 }
1121}
1122
1123impl<'a> FromKclValue<'a> for SweepPath {
1124 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1125 let case1 = Sketch::from_kcl_val;
1126 let case2 = <Vec<Sketch>>::from_kcl_val;
1127 let case3 = Helix::from_kcl_val;
1128 case1(arg)
1129 .map(Self::Sketch)
1130 .or_else(|| case2(arg).map(|arg0: Vec<Sketch>| Self::Sketch(arg0[0].clone())))
1131 .or_else(|| case3(arg).map(|arg0: Helix| Self::Helix(Box::new(arg0))))
1132 }
1133}
1134impl<'a> FromKclValue<'a> for String {
1135 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1136 let KclValue::String { value, meta: _ } = arg else {
1137 return None;
1138 };
1139 Some(value.to_owned())
1140 }
1141}
1142impl<'a> FromKclValue<'a> for crate::parsing::ast::types::KclNone {
1143 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1144 let KclValue::KclNone { value, meta: _ } = arg else {
1145 return None;
1146 };
1147 Some(value.to_owned())
1148 }
1149}
1150impl<'a> FromKclValue<'a> for bool {
1151 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1152 let KclValue::Bool { value, meta: _ } = arg else {
1153 return None;
1154 };
1155 Some(*value)
1156 }
1157}
1158
1159impl<'a> FromKclValue<'a> for Box<Solid> {
1160 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1161 let KclValue::Solid { value } = arg else {
1162 return None;
1163 };
1164 Some(value.to_owned())
1165 }
1166}
1167
1168impl<'a> FromKclValue<'a> for FunctionSource {
1169 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1170 arg.as_function().cloned()
1171 }
1172}
1173
1174impl<'a> FromKclValue<'a> for SketchOrSurface {
1175 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1176 match arg {
1177 KclValue::Sketch { value: sg } => Some(Self::Sketch(sg.to_owned())),
1178 KclValue::Plane { value } => Some(Self::SketchSurface(SketchSurface::Plane(value.clone()))),
1179 KclValue::Face { value } => Some(Self::SketchSurface(SketchSurface::Face(value.clone()))),
1180 _ => None,
1181 }
1182 }
1183}
1184impl<'a> FromKclValue<'a> for SketchSurface {
1185 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1186 match arg {
1187 KclValue::Plane { value } => Some(Self::Plane(value.clone())),
1188 KclValue::Face { value } => Some(Self::Face(value.clone())),
1189 _ => None,
1190 }
1191 }
1192}
1193
1194impl From<Args> for Metadata {
1195 fn from(value: Args) -> Self {
1196 Self {
1197 source_range: value.source_range,
1198 }
1199 }
1200}
1201
1202impl From<Args> for Vec<Metadata> {
1203 fn from(value: Args) -> Self {
1204 vec![Metadata {
1205 source_range: value.source_range,
1206 }]
1207 }
1208}