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