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