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