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