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 let tags = arg
475 .clone()
476 .into_array()
477 .iter()
478 .map(|v| v.get_tag_identifier().unwrap())
479 .collect();
480 Some(tags)
481 }
482}
483
484impl<'a> FromKclValue<'a> for Vec<KclValue> {
485 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
486 Some(arg.clone().into_array())
487 }
488}
489
490impl<'a> FromKclValue<'a> for KclValue {
491 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
492 Some(arg.clone())
493 }
494}
495
496macro_rules! let_field_of {
497 ($obj:ident, $field:ident?) => {
499 let $field = $obj.get(stringify!($field)).and_then(FromKclValue::from_kcl_val);
500 };
501 ($obj:ident, $field:ident? $key:literal) => {
503 let $field = $obj.get($key).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 $(, $annotation:ty)?) => {
511 let $field $(: $annotation)? = $obj.get(stringify!($field)).and_then(FromKclValue::from_kcl_val)?;
512 };
513}
514
515impl<'a> FromKclValue<'a> for crate::execution::Plane {
516 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
517 arg.as_plane().cloned()
518 }
519}
520
521impl<'a> FromKclValue<'a> for crate::execution::PlaneType {
522 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
523 let plane_type = match arg.as_str()? {
524 "XY" | "xy" => Self::XY,
525 "XZ" | "xz" => Self::XZ,
526 "YZ" | "yz" => Self::YZ,
527 "Custom" => Self::Custom,
528 _ => return None,
529 };
530 Some(plane_type)
531 }
532}
533
534impl<'a> FromKclValue<'a> for kittycad_modeling_cmds::units::UnitLength {
535 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
536 let s = arg.as_str()?;
537 s.parse().ok()
538 }
539}
540
541impl<'a> FromKclValue<'a> for kittycad_modeling_cmds::coord::System {
542 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
543 let obj = arg.as_object()?;
544 let_field_of!(obj, forward);
545 let_field_of!(obj, up);
546 Some(Self { forward, up })
547 }
548}
549
550impl<'a> FromKclValue<'a> for kittycad_modeling_cmds::coord::AxisDirectionPair {
551 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
552 let obj = arg.as_object()?;
553 let_field_of!(obj, axis);
554 let_field_of!(obj, direction);
555 Some(Self { axis, direction })
556 }
557}
558
559impl<'a> FromKclValue<'a> for kittycad_modeling_cmds::coord::Axis {
560 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
561 let s = arg.as_str()?;
562 match s {
563 "y" => Some(Self::Y),
564 "z" => Some(Self::Z),
565 _ => None,
566 }
567 }
568}
569
570impl<'a> FromKclValue<'a> for PolygonType {
571 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
572 let s = arg.as_str()?;
573 match s {
574 "inscribed" => Some(Self::Inscribed),
575 _ => Some(Self::Circumscribed),
576 }
577 }
578}
579
580impl<'a> FromKclValue<'a> for kittycad_modeling_cmds::coord::Direction {
581 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
582 let s = arg.as_str()?;
583 match s {
584 "positive" => Some(Self::Positive),
585 "negative" => Some(Self::Negative),
586 _ => None,
587 }
588 }
589}
590
591impl<'a> FromKclValue<'a> for crate::execution::Geometry {
592 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
593 match arg {
594 KclValue::Sketch { value } => Some(Self::Sketch(*value.to_owned())),
595 KclValue::Solid { value } => Some(Self::Solid(*value.to_owned())),
596 _ => None,
597 }
598 }
599}
600
601impl<'a> FromKclValue<'a> for crate::execution::GeometryWithImportedGeometry {
602 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
603 match arg {
604 KclValue::Sketch { value } => Some(Self::Sketch(*value.to_owned())),
605 KclValue::Solid { value } => Some(Self::Solid(*value.to_owned())),
606 KclValue::ImportedGeometry(value) => Some(Self::ImportedGeometry(Box::new(value.clone()))),
607 _ => None,
608 }
609 }
610}
611
612impl<'a> FromKclValue<'a> for FaceTag {
613 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
614 let case1 = || match arg.as_str() {
615 Some("start" | "START") => Some(Self::StartOrEnd(super::sketch::StartOrEnd::Start)),
616 Some("end" | "END") => Some(Self::StartOrEnd(super::sketch::StartOrEnd::End)),
617 _ => None,
618 };
619 let case2 = || {
620 let tag = TagIdentifier::from_kcl_val(arg)?;
621 Some(Self::Tag(Box::new(tag)))
622 };
623 case1().or_else(case2)
624 }
625}
626
627impl<'a> FromKclValue<'a> for super::sketch::TangentialArcData {
628 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
629 let obj = arg.as_object()?;
630 let_field_of!(obj, radius);
631 let_field_of!(obj, offset);
632 Some(Self::RadiusAndOffset { radius, offset })
633 }
634}
635
636impl<'a> FromKclValue<'a> for crate::execution::Point3d {
637 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
638 if let Some(obj) = arg.as_object() {
640 let_field_of!(obj, x, TyF64);
641 let_field_of!(obj, y, TyF64);
642 let_field_of!(obj, z, TyF64);
643 let (a, ty) = NumericType::combine_eq_array(&[x, y, z]);
645 return Some(Self {
646 x: a[0],
647 y: a[1],
648 z: a[2],
649 units: ty.as_length(),
650 });
651 }
652 let [x, y, z]: [TyF64; 3] = FromKclValue::from_kcl_val(arg)?;
654 let (a, ty) = NumericType::combine_eq_array(&[x, y, z]);
655 Some(Self {
656 x: a[0],
657 y: a[1],
658 z: a[2],
659 units: ty.as_length(),
660 })
661 }
662}
663
664impl<'a> FromKclValue<'a> for super::sketch::PlaneData {
665 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
666 if let KclValue::Plane { value } = arg {
668 return Some(Self::Plane(PlaneInfo {
669 origin: value.info.origin,
670 x_axis: value.info.x_axis,
671 y_axis: value.info.y_axis,
672 z_axis: value.info.z_axis,
673 }));
674 }
675 if let Some(s) = arg.as_str() {
677 return match s {
678 "XY" | "xy" => Some(Self::XY),
679 "-XY" | "-xy" => Some(Self::NegXY),
680 "XZ" | "xz" => Some(Self::XZ),
681 "-XZ" | "-xz" => Some(Self::NegXZ),
682 "YZ" | "yz" => Some(Self::YZ),
683 "-YZ" | "-yz" => Some(Self::NegYZ),
684 _ => None,
685 };
686 }
687 let obj = arg.as_object()?;
689 let_field_of!(obj, plane, &KclObjectFields);
690 let origin = plane.get("origin").and_then(FromKclValue::from_kcl_val)?;
691 let x_axis: crate::execution::Point3d = plane.get("xAxis").and_then(FromKclValue::from_kcl_val)?;
692 let y_axis = plane.get("yAxis").and_then(FromKclValue::from_kcl_val)?;
693 let z_axis = x_axis.axes_cross_product(&y_axis);
694 Some(Self::Plane(PlaneInfo {
695 origin,
696 x_axis,
697 y_axis,
698 z_axis,
699 }))
700 }
701}
702
703impl<'a> FromKclValue<'a> for crate::execution::ExtrudePlane {
704 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
705 let obj = arg.as_object()?;
706 let_field_of!(obj, face_id "faceId");
707 let tag = FromKclValue::from_kcl_val(obj.get("tag")?);
708 let_field_of!(obj, geo_meta "geoMeta");
709 Some(Self { face_id, tag, geo_meta })
710 }
711}
712
713impl<'a> FromKclValue<'a> for crate::execution::ExtrudeArc {
714 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
715 let obj = arg.as_object()?;
716 let_field_of!(obj, face_id "faceId");
717 let tag = FromKclValue::from_kcl_val(obj.get("tag")?);
718 let_field_of!(obj, geo_meta "geoMeta");
719 Some(Self { face_id, tag, geo_meta })
720 }
721}
722
723impl<'a> FromKclValue<'a> for crate::execution::GeoMeta {
724 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
725 let obj = arg.as_object()?;
726 let_field_of!(obj, id);
727 let_field_of!(obj, source_range "sourceRange");
728 Some(Self {
729 id,
730 metadata: Metadata { source_range },
731 })
732 }
733}
734
735impl<'a> FromKclValue<'a> for crate::execution::ChamferSurface {
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::FilletSurface {
746 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
747 let obj = arg.as_object()?;
748 let_field_of!(obj, face_id "faceId");
749 let tag = FromKclValue::from_kcl_val(obj.get("tag")?);
750 let_field_of!(obj, geo_meta "geoMeta");
751 Some(Self { face_id, tag, geo_meta })
752 }
753}
754
755impl<'a> FromKclValue<'a> for ExtrudeSurface {
756 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
757 let case1 = crate::execution::ExtrudePlane::from_kcl_val;
758 let case2 = crate::execution::ExtrudeArc::from_kcl_val;
759 let case3 = crate::execution::ChamferSurface::from_kcl_val;
760 let case4 = crate::execution::FilletSurface::from_kcl_val;
761 case1(arg)
762 .map(Self::ExtrudePlane)
763 .or_else(|| case2(arg).map(Self::ExtrudeArc))
764 .or_else(|| case3(arg).map(Self::Chamfer))
765 .or_else(|| case4(arg).map(Self::Fillet))
766 }
767}
768
769impl<'a> FromKclValue<'a> for crate::execution::EdgeCut {
770 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
771 let obj = arg.as_object()?;
772 let_field_of!(obj, typ "type");
773 let tag = Box::new(obj.get("tag").and_then(FromKclValue::from_kcl_val));
774 let_field_of!(obj, edge_id "edgeId");
775 let_field_of!(obj, id);
776 match typ {
777 "fillet" => {
778 let_field_of!(obj, radius);
779 Some(Self::Fillet {
780 edge_id,
781 tag,
782 id,
783 radius,
784 })
785 }
786 "chamfer" => {
787 let_field_of!(obj, length);
788 Some(Self::Chamfer {
789 id,
790 length,
791 edge_id,
792 tag,
793 })
794 }
795 _ => None,
796 }
797 }
798}
799
800macro_rules! impl_from_kcl_for_vec {
801 ($typ:path) => {
802 impl<'a> FromKclValue<'a> for Vec<$typ> {
803 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
804 arg.clone()
805 .into_array()
806 .iter()
807 .map(|value| FromKclValue::from_kcl_val(value))
808 .collect::<Option<_>>()
809 }
810 }
811 };
812}
813
814impl_from_kcl_for_vec!(FaceTag);
815impl_from_kcl_for_vec!(crate::execution::EdgeCut);
816impl_from_kcl_for_vec!(crate::execution::Metadata);
817impl_from_kcl_for_vec!(super::fillet::EdgeReference);
818impl_from_kcl_for_vec!(ExtrudeSurface);
819impl_from_kcl_for_vec!(TyF64);
820impl_from_kcl_for_vec!(Solid);
821impl_from_kcl_for_vec!(Sketch);
822
823impl<'a> FromKclValue<'a> for SourceRange {
824 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
825 let value = match arg {
826 KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } => value,
827 _ => {
828 return None;
829 }
830 };
831 if value.len() != 3 {
832 return None;
833 }
834 let v0 = value.first()?;
835 let v1 = value.get(1)?;
836 let v2 = value.get(2)?;
837 Some(SourceRange::new(
838 v0.as_usize()?,
839 v1.as_usize()?,
840 ModuleId::from_usize(v2.as_usize()?),
841 ))
842 }
843}
844
845impl<'a> FromKclValue<'a> for crate::execution::Metadata {
846 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
847 FromKclValue::from_kcl_val(arg).map(|sr| Self { source_range: sr })
848 }
849}
850
851impl<'a> FromKclValue<'a> for crate::execution::Solid {
852 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
853 arg.as_solid().cloned()
854 }
855}
856
857impl<'a> FromKclValue<'a> for crate::execution::SolidOrSketchOrImportedGeometry {
858 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
859 match arg {
860 KclValue::Solid { value } => Some(Self::SolidSet(vec![(**value).clone()])),
861 KclValue::Sketch { value } => Some(Self::SketchSet(vec![(**value).clone()])),
862 KclValue::HomArray { value, .. } => {
863 let mut solids = vec![];
864 let mut sketches = vec![];
865 for item in value {
866 match item {
867 KclValue::Solid { value } => solids.push((**value).clone()),
868 KclValue::Sketch { value } => sketches.push((**value).clone()),
869 _ => return None,
870 }
871 }
872 if !solids.is_empty() {
873 Some(Self::SolidSet(solids))
874 } else {
875 Some(Self::SketchSet(sketches))
876 }
877 }
878 KclValue::ImportedGeometry(value) => Some(Self::ImportedGeometry(Box::new(value.clone()))),
879 _ => None,
880 }
881 }
882}
883
884impl<'a> FromKclValue<'a> for crate::execution::SolidOrImportedGeometry {
885 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
886 match arg {
887 KclValue::Solid { value } => Some(Self::SolidSet(vec![(**value).clone()])),
888 KclValue::HomArray { value, .. } => {
889 let mut solids = vec![];
890 for item in value {
891 match item {
892 KclValue::Solid { value } => solids.push((**value).clone()),
893 _ => return None,
894 }
895 }
896 Some(Self::SolidSet(solids))
897 }
898 KclValue::ImportedGeometry(value) => Some(Self::ImportedGeometry(Box::new(value.clone()))),
899 _ => None,
900 }
901 }
902}
903
904impl<'a> FromKclValue<'a> for super::sketch::SketchData {
905 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
906 let case1 = crate::execution::Plane::from_kcl_val;
908 let case2 = super::sketch::PlaneData::from_kcl_val;
909 let case3 = crate::execution::Solid::from_kcl_val;
910 let case4 = <Vec<Solid>>::from_kcl_val;
911 case1(arg)
912 .map(Box::new)
913 .map(Self::Plane)
914 .or_else(|| case2(arg).map(Self::PlaneOrientation))
915 .or_else(|| case3(arg).map(Box::new).map(Self::Solid))
916 .or_else(|| case4(arg).map(|v| Box::new(v[0].clone())).map(Self::Solid))
917 }
918}
919
920impl<'a> FromKclValue<'a> for super::fillet::EdgeReference {
921 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
922 let id = arg.as_uuid().map(Self::Uuid);
923 let tag = || TagIdentifier::from_kcl_val(arg).map(Box::new).map(Self::Tag);
924 id.or_else(tag)
925 }
926}
927
928impl<'a> FromKclValue<'a> for super::axis_or_reference::Axis2dOrEdgeReference {
929 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
930 let case1 = |arg: &KclValue| {
931 let obj = arg.as_object()?;
932 let_field_of!(obj, direction);
933 let_field_of!(obj, origin);
934 Some(Self::Axis { direction, origin })
935 };
936 let case2 = super::fillet::EdgeReference::from_kcl_val;
937 case1(arg).or_else(|| case2(arg).map(Self::Edge))
938 }
939}
940
941impl<'a> FromKclValue<'a> for super::axis_or_reference::Axis3dOrEdgeReference {
942 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
943 let case1 = |arg: &KclValue| {
944 let obj = arg.as_object()?;
945 let_field_of!(obj, direction);
946 let_field_of!(obj, origin);
947 Some(Self::Axis { direction, origin })
948 };
949 let case2 = super::fillet::EdgeReference::from_kcl_val;
950 case1(arg).or_else(|| case2(arg).map(Self::Edge))
951 }
952}
953
954impl<'a> FromKclValue<'a> for super::axis_or_reference::Axis2dOrPoint2d {
955 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
956 let case1 = |arg: &KclValue| {
957 let obj = arg.as_object()?;
958 let_field_of!(obj, direction);
959 let_field_of!(obj, origin);
960 Some(Self::Axis { direction, origin })
961 };
962 let case2 = <[TyF64; 2]>::from_kcl_val;
963 case1(arg).or_else(|| case2(arg).map(Self::Point))
964 }
965}
966
967impl<'a> FromKclValue<'a> for super::axis_or_reference::Axis3dOrPoint3d {
968 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
969 let case1 = |arg: &KclValue| {
970 let obj = arg.as_object()?;
971 let_field_of!(obj, direction);
972 let_field_of!(obj, origin);
973 Some(Self::Axis { direction, origin })
974 };
975 let case2 = <[TyF64; 3]>::from_kcl_val;
976 case1(arg).or_else(|| case2(arg).map(Self::Point))
977 }
978}
979
980impl<'a> FromKclValue<'a> for super::axis_or_reference::Point3dAxis3dOrGeometryReference {
981 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
982 let case1 = |arg: &KclValue| {
983 let obj = arg.as_object()?;
984 let_field_of!(obj, direction);
985 let_field_of!(obj, origin);
986 Some(Self::Axis { direction, origin })
987 };
988 let case2 = <[TyF64; 3]>::from_kcl_val;
989 let case3 = super::fillet::EdgeReference::from_kcl_val;
990 let case4 = FaceTag::from_kcl_val;
991 let case5 = Box::<Solid>::from_kcl_val;
992 let case6 = TagIdentifier::from_kcl_val;
993 let case7 = Box::<Plane>::from_kcl_val;
994 let case8 = Box::<Sketch>::from_kcl_val;
995
996 case1(arg)
997 .or_else(|| case2(arg).map(Self::Point))
998 .or_else(|| case3(arg).map(Self::Edge))
999 .or_else(|| case4(arg).map(Self::Face))
1000 .or_else(|| case5(arg).map(Self::Solid))
1001 .or_else(|| case6(arg).map(Self::TaggedEdgeOrFace))
1002 .or_else(|| case7(arg).map(Self::Plane))
1003 .or_else(|| case8(arg).map(Self::Sketch))
1004 }
1005}
1006
1007impl<'a> FromKclValue<'a> for i64 {
1008 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1009 match arg {
1010 KclValue::Number { value, .. } => crate::try_f64_to_i64(*value),
1011 _ => None,
1012 }
1013 }
1014}
1015
1016impl<'a> FromKclValue<'a> for &'a str {
1017 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1018 let KclValue::String { value, meta: _ } = arg else {
1019 return None;
1020 };
1021 Some(value)
1022 }
1023}
1024
1025impl<'a> FromKclValue<'a> for &'a KclObjectFields {
1026 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1027 let KclValue::Object { value, .. } = arg else {
1028 return None;
1029 };
1030 Some(value)
1031 }
1032}
1033
1034impl<'a> FromKclValue<'a> for uuid::Uuid {
1035 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1036 let KclValue::Uuid { value, meta: _ } = arg else {
1037 return None;
1038 };
1039 Some(*value)
1040 }
1041}
1042
1043impl<'a> FromKclValue<'a> for u32 {
1044 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1045 match arg {
1046 KclValue::Number { value, .. } => crate::try_f64_to_u32(*value),
1047 _ => None,
1048 }
1049 }
1050}
1051
1052impl<'a> FromKclValue<'a> for NonZeroU32 {
1053 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1054 u32::from_kcl_val(arg).and_then(|x| x.try_into().ok())
1055 }
1056}
1057
1058impl<'a> FromKclValue<'a> for u64 {
1059 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1060 match arg {
1061 KclValue::Number { value, .. } => crate::try_f64_to_u64(*value),
1062 _ => None,
1063 }
1064 }
1065}
1066
1067impl<'a> FromKclValue<'a> for TyF64 {
1068 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1069 match arg {
1070 KclValue::Number { value, ty, .. } => Some(TyF64::new(*value, *ty)),
1071 _ => None,
1072 }
1073 }
1074}
1075
1076impl<'a> FromKclValue<'a> for [TyF64; 2] {
1077 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1078 match arg {
1079 KclValue::Tuple { value, meta: _ } | KclValue::HomArray { value, .. } => {
1080 if value.len() != 2 {
1081 return None;
1082 }
1083 let v0 = value.first()?;
1084 let v1 = value.get(1)?;
1085 let array = [v0.as_ty_f64()?, v1.as_ty_f64()?];
1086 Some(array)
1087 }
1088 _ => None,
1089 }
1090 }
1091}
1092
1093impl<'a> FromKclValue<'a> for [TyF64; 3] {
1094 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1095 match arg {
1096 KclValue::Tuple { value, meta: _ } | KclValue::HomArray { value, .. } => {
1097 if value.len() != 3 {
1098 return None;
1099 }
1100 let v0 = value.first()?;
1101 let v1 = value.get(1)?;
1102 let v2 = value.get(2)?;
1103 let array = [v0.as_ty_f64()?, v1.as_ty_f64()?, v2.as_ty_f64()?];
1104 Some(array)
1105 }
1106 _ => None,
1107 }
1108 }
1109}
1110
1111impl<'a> FromKclValue<'a> for [TyF64; 6] {
1112 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1113 match arg {
1114 KclValue::Tuple { value, meta: _ } | KclValue::HomArray { value, .. } => {
1115 if value.len() != 6 {
1116 return None;
1117 }
1118 let v0 = value.first()?;
1119 let v1 = value.get(1)?;
1120 let v2 = value.get(2)?;
1121 let v3 = value.get(3)?;
1122 let v4 = value.get(4)?;
1123 let v5 = value.get(5)?;
1124 let array = [
1125 v0.as_ty_f64()?,
1126 v1.as_ty_f64()?,
1127 v2.as_ty_f64()?,
1128 v3.as_ty_f64()?,
1129 v4.as_ty_f64()?,
1130 v5.as_ty_f64()?,
1131 ];
1132 Some(array)
1133 }
1134 _ => None,
1135 }
1136 }
1137}
1138
1139impl<'a> FromKclValue<'a> for Sketch {
1140 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1141 let KclValue::Sketch { value } = arg else {
1142 return None;
1143 };
1144 Some(value.as_ref().to_owned())
1145 }
1146}
1147
1148impl<'a> FromKclValue<'a> for Helix {
1149 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1150 let KclValue::Helix { value } = arg else {
1151 return None;
1152 };
1153 Some(value.as_ref().to_owned())
1154 }
1155}
1156
1157impl<'a> FromKclValue<'a> for SweepPath {
1158 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1159 let case1 = Sketch::from_kcl_val;
1160 let case2 = <Vec<Sketch>>::from_kcl_val;
1161 let case3 = Helix::from_kcl_val;
1162 case1(arg)
1163 .map(Self::Sketch)
1164 .or_else(|| case2(arg).map(|arg0: Vec<Sketch>| Self::Sketch(arg0[0].clone())))
1165 .or_else(|| case3(arg).map(|arg0: Helix| Self::Helix(Box::new(arg0))))
1166 }
1167}
1168impl<'a> FromKclValue<'a> for String {
1169 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1170 let KclValue::String { value, meta: _ } = arg else {
1171 return None;
1172 };
1173 Some(value.to_owned())
1174 }
1175}
1176impl<'a> FromKclValue<'a> for crate::parsing::ast::types::KclNone {
1177 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1178 let KclValue::KclNone { value, meta: _ } = arg else {
1179 return None;
1180 };
1181 Some(value.to_owned())
1182 }
1183}
1184impl<'a> FromKclValue<'a> for bool {
1185 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1186 let KclValue::Bool { value, meta: _ } = arg else {
1187 return None;
1188 };
1189 Some(*value)
1190 }
1191}
1192
1193impl<'a> FromKclValue<'a> for Box<Solid> {
1194 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1195 let KclValue::Solid { value } = arg else {
1196 return None;
1197 };
1198 Some(value.to_owned())
1199 }
1200}
1201
1202impl<'a> FromKclValue<'a> for Box<Plane> {
1203 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1204 let KclValue::Plane { value } = arg else {
1205 return None;
1206 };
1207 Some(value.to_owned())
1208 }
1209}
1210
1211impl<'a> FromKclValue<'a> for Box<Sketch> {
1212 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1213 let KclValue::Sketch { value } = arg else {
1214 return None;
1215 };
1216 Some(value.to_owned())
1217 }
1218}
1219
1220impl<'a> FromKclValue<'a> for FunctionSource {
1221 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1222 arg.as_function().cloned()
1223 }
1224}
1225
1226impl<'a> FromKclValue<'a> for SketchOrSurface {
1227 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1228 match arg {
1229 KclValue::Sketch { value: sg } => Some(Self::Sketch(sg.to_owned())),
1230 KclValue::Plane { value } => Some(Self::SketchSurface(SketchSurface::Plane(value.clone()))),
1231 KclValue::Face { value } => Some(Self::SketchSurface(SketchSurface::Face(value.clone()))),
1232 _ => None,
1233 }
1234 }
1235}
1236impl<'a> FromKclValue<'a> for SketchSurface {
1237 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1238 match arg {
1239 KclValue::Plane { value } => Some(Self::Plane(value.clone())),
1240 KclValue::Face { value } => Some(Self::Face(value.clone())),
1241 _ => None,
1242 }
1243 }
1244}
1245
1246impl From<Args> for Metadata {
1247 fn from(value: Args) -> Self {
1248 Self {
1249 source_range: value.source_range,
1250 }
1251 }
1252}
1253
1254impl From<Args> for Vec<Metadata> {
1255 fn from(value: Args) -> Self {
1256 vec![Metadata {
1257 source_range: value.source_range,
1258 }]
1259 }
1260}