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 }));
678 }
679 if let Some(s) = arg.as_str() {
681 return match s {
682 "XY" | "xy" => Some(Self::XY),
683 "-XY" | "-xy" => Some(Self::NegXY),
684 "XZ" | "xz" => Some(Self::XZ),
685 "-XZ" | "-xz" => Some(Self::NegXZ),
686 "YZ" | "yz" => Some(Self::YZ),
687 "-YZ" | "-yz" => Some(Self::NegYZ),
688 _ => None,
689 };
690 }
691 let obj = arg.as_object()?;
693 let_field_of!(obj, plane, &KclObjectFields);
694 let origin = plane.get("origin").and_then(FromKclValue::from_kcl_val)?;
695 let x_axis = plane.get("xAxis").and_then(FromKclValue::from_kcl_val)?;
696 let y_axis = plane.get("yAxis").and_then(FromKclValue::from_kcl_val)?;
697 Some(Self::Plane(PlaneInfo { origin, x_axis, y_axis }))
698 }
699}
700
701impl<'a> FromKclValue<'a> for crate::execution::ExtrudePlane {
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::ExtrudeArc {
712 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
713 let obj = arg.as_object()?;
714 let_field_of!(obj, face_id "faceId");
715 let tag = FromKclValue::from_kcl_val(obj.get("tag")?);
716 let_field_of!(obj, geo_meta "geoMeta");
717 Some(Self { face_id, tag, geo_meta })
718 }
719}
720
721impl<'a> FromKclValue<'a> for crate::execution::GeoMeta {
722 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
723 let obj = arg.as_object()?;
724 let_field_of!(obj, id);
725 let_field_of!(obj, source_range "sourceRange");
726 Some(Self {
727 id,
728 metadata: Metadata { source_range },
729 })
730 }
731}
732
733impl<'a> FromKclValue<'a> for crate::execution::ChamferSurface {
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 crate::execution::FilletSurface {
744 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
745 let obj = arg.as_object()?;
746 let_field_of!(obj, face_id "faceId");
747 let tag = FromKclValue::from_kcl_val(obj.get("tag")?);
748 let_field_of!(obj, geo_meta "geoMeta");
749 Some(Self { face_id, tag, geo_meta })
750 }
751}
752
753impl<'a> FromKclValue<'a> for ExtrudeSurface {
754 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
755 let case1 = crate::execution::ExtrudePlane::from_kcl_val;
756 let case2 = crate::execution::ExtrudeArc::from_kcl_val;
757 let case3 = crate::execution::ChamferSurface::from_kcl_val;
758 let case4 = crate::execution::FilletSurface::from_kcl_val;
759 case1(arg)
760 .map(Self::ExtrudePlane)
761 .or_else(|| case2(arg).map(Self::ExtrudeArc))
762 .or_else(|| case3(arg).map(Self::Chamfer))
763 .or_else(|| case4(arg).map(Self::Fillet))
764 }
765}
766
767impl<'a> FromKclValue<'a> for crate::execution::EdgeCut {
768 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
769 let obj = arg.as_object()?;
770 let_field_of!(obj, typ "type");
771 let tag = Box::new(obj.get("tag").and_then(FromKclValue::from_kcl_val));
772 let_field_of!(obj, edge_id "edgeId");
773 let_field_of!(obj, id);
774 match typ {
775 "fillet" => {
776 let_field_of!(obj, radius);
777 Some(Self::Fillet {
778 edge_id,
779 tag,
780 id,
781 radius,
782 })
783 }
784 "chamfer" => {
785 let_field_of!(obj, length);
786 Some(Self::Chamfer {
787 id,
788 length,
789 edge_id,
790 tag,
791 })
792 }
793 _ => None,
794 }
795 }
796}
797
798macro_rules! impl_from_kcl_for_vec {
799 ($typ:path) => {
800 impl<'a> FromKclValue<'a> for Vec<$typ> {
801 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
802 arg.clone()
803 .into_array()
804 .iter()
805 .map(|value| FromKclValue::from_kcl_val(value))
806 .collect::<Option<_>>()
807 }
808 }
809 };
810}
811
812impl_from_kcl_for_vec!(FaceTag);
813impl_from_kcl_for_vec!(crate::execution::EdgeCut);
814impl_from_kcl_for_vec!(crate::execution::Metadata);
815impl_from_kcl_for_vec!(super::fillet::EdgeReference);
816impl_from_kcl_for_vec!(ExtrudeSurface);
817impl_from_kcl_for_vec!(TyF64);
818impl_from_kcl_for_vec!(Solid);
819impl_from_kcl_for_vec!(Sketch);
820
821impl<'a> FromKclValue<'a> for SourceRange {
822 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
823 let value = match arg {
824 KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } => value,
825 _ => {
826 return None;
827 }
828 };
829 if value.len() != 3 {
830 return None;
831 }
832 let v0 = value.first()?;
833 let v1 = value.get(1)?;
834 let v2 = value.get(2)?;
835 Some(SourceRange::new(
836 v0.as_usize()?,
837 v1.as_usize()?,
838 ModuleId::from_usize(v2.as_usize()?),
839 ))
840 }
841}
842
843impl<'a> FromKclValue<'a> for crate::execution::Metadata {
844 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
845 FromKclValue::from_kcl_val(arg).map(|sr| Self { source_range: sr })
846 }
847}
848
849impl<'a> FromKclValue<'a> for crate::execution::Solid {
850 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
851 arg.as_solid().cloned()
852 }
853}
854
855impl<'a> FromKclValue<'a> for crate::execution::SolidOrSketchOrImportedGeometry {
856 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
857 match arg {
858 KclValue::Solid { value } => Some(Self::SolidSet(vec![(**value).clone()])),
859 KclValue::Sketch { value } => Some(Self::SketchSet(vec![(**value).clone()])),
860 KclValue::HomArray { value, .. } => {
861 let mut solids = vec![];
862 let mut sketches = vec![];
863 for item in value {
864 match item {
865 KclValue::Solid { value } => solids.push((**value).clone()),
866 KclValue::Sketch { value } => sketches.push((**value).clone()),
867 _ => return None,
868 }
869 }
870 if !solids.is_empty() {
871 Some(Self::SolidSet(solids))
872 } else {
873 Some(Self::SketchSet(sketches))
874 }
875 }
876 KclValue::ImportedGeometry(value) => Some(Self::ImportedGeometry(Box::new(value.clone()))),
877 _ => None,
878 }
879 }
880}
881
882impl<'a> FromKclValue<'a> for crate::execution::SolidOrImportedGeometry {
883 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
884 match arg {
885 KclValue::Solid { value } => Some(Self::SolidSet(vec![(**value).clone()])),
886 KclValue::HomArray { value, .. } => {
887 let mut solids = vec![];
888 for item in value {
889 match item {
890 KclValue::Solid { value } => solids.push((**value).clone()),
891 _ => return None,
892 }
893 }
894 Some(Self::SolidSet(solids))
895 }
896 KclValue::ImportedGeometry(value) => Some(Self::ImportedGeometry(Box::new(value.clone()))),
897 _ => None,
898 }
899 }
900}
901
902impl<'a> FromKclValue<'a> for super::sketch::SketchData {
903 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
904 let case1 = crate::execution::Plane::from_kcl_val;
906 let case2 = super::sketch::PlaneData::from_kcl_val;
907 let case3 = crate::execution::Solid::from_kcl_val;
908 let case4 = <Vec<Solid>>::from_kcl_val;
909 case1(arg)
910 .map(Box::new)
911 .map(Self::Plane)
912 .or_else(|| case2(arg).map(Self::PlaneOrientation))
913 .or_else(|| case3(arg).map(Box::new).map(Self::Solid))
914 .or_else(|| case4(arg).map(|v| Box::new(v[0].clone())).map(Self::Solid))
915 }
916}
917
918impl<'a> FromKclValue<'a> for super::fillet::EdgeReference {
919 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
920 let id = arg.as_uuid().map(Self::Uuid);
921 let tag = || TagIdentifier::from_kcl_val(arg).map(Box::new).map(Self::Tag);
922 id.or_else(tag)
923 }
924}
925
926impl<'a> FromKclValue<'a> for super::axis_or_reference::Axis2dOrEdgeReference {
927 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
928 let case1 = |arg: &KclValue| {
929 let obj = arg.as_object()?;
930 let_field_of!(obj, direction);
931 let_field_of!(obj, origin);
932 Some(Self::Axis { direction, origin })
933 };
934 let case2 = super::fillet::EdgeReference::from_kcl_val;
935 case1(arg).or_else(|| case2(arg).map(Self::Edge))
936 }
937}
938
939impl<'a> FromKclValue<'a> for super::axis_or_reference::Axis3dOrEdgeReference {
940 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
941 let case1 = |arg: &KclValue| {
942 let obj = arg.as_object()?;
943 let_field_of!(obj, direction);
944 let_field_of!(obj, origin);
945 Some(Self::Axis { direction, origin })
946 };
947 let case2 = super::fillet::EdgeReference::from_kcl_val;
948 case1(arg).or_else(|| case2(arg).map(Self::Edge))
949 }
950}
951
952impl<'a> FromKclValue<'a> for super::axis_or_reference::Axis2dOrPoint2d {
953 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
954 let case1 = |arg: &KclValue| {
955 let obj = arg.as_object()?;
956 let_field_of!(obj, direction);
957 let_field_of!(obj, origin);
958 Some(Self::Axis { direction, origin })
959 };
960 let case2 = <[TyF64; 2]>::from_kcl_val;
961 case1(arg).or_else(|| case2(arg).map(Self::Point))
962 }
963}
964
965impl<'a> FromKclValue<'a> for super::axis_or_reference::Axis3dOrPoint3d {
966 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
967 let case1 = |arg: &KclValue| {
968 let obj = arg.as_object()?;
969 let_field_of!(obj, direction);
970 let_field_of!(obj, origin);
971 Some(Self::Axis { direction, origin })
972 };
973 let case2 = <[TyF64; 3]>::from_kcl_val;
974 case1(arg).or_else(|| case2(arg).map(Self::Point))
975 }
976}
977
978impl<'a> FromKclValue<'a> for i64 {
979 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
980 match arg {
981 KclValue::Number { value, .. } => crate::try_f64_to_i64(*value),
982 _ => None,
983 }
984 }
985}
986
987impl<'a> FromKclValue<'a> for &'a str {
988 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
989 let KclValue::String { value, meta: _ } = arg else {
990 return None;
991 };
992 Some(value)
993 }
994}
995
996impl<'a> FromKclValue<'a> for &'a KclObjectFields {
997 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
998 let KclValue::Object { value, meta: _ } = arg else {
999 return None;
1000 };
1001 Some(value)
1002 }
1003}
1004
1005impl<'a> FromKclValue<'a> for uuid::Uuid {
1006 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1007 let KclValue::Uuid { value, meta: _ } = arg else {
1008 return None;
1009 };
1010 Some(*value)
1011 }
1012}
1013
1014impl<'a> FromKclValue<'a> for u32 {
1015 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1016 match arg {
1017 KclValue::Number { value, .. } => crate::try_f64_to_u32(*value),
1018 _ => None,
1019 }
1020 }
1021}
1022
1023impl<'a> FromKclValue<'a> for NonZeroU32 {
1024 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1025 u32::from_kcl_val(arg).and_then(|x| x.try_into().ok())
1026 }
1027}
1028
1029impl<'a> FromKclValue<'a> for u64 {
1030 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1031 match arg {
1032 KclValue::Number { value, .. } => crate::try_f64_to_u64(*value),
1033 _ => None,
1034 }
1035 }
1036}
1037
1038impl<'a> FromKclValue<'a> for TyF64 {
1039 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1040 match arg {
1041 KclValue::Number { value, ty, .. } => Some(TyF64::new(*value, *ty)),
1042 _ => None,
1043 }
1044 }
1045}
1046
1047impl<'a> FromKclValue<'a> for [TyF64; 2] {
1048 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1049 match arg {
1050 KclValue::Tuple { value, meta: _ } | KclValue::HomArray { value, .. } => {
1051 if value.len() != 2 {
1052 return None;
1053 }
1054 let v0 = value.first()?;
1055 let v1 = value.get(1)?;
1056 let array = [v0.as_ty_f64()?, v1.as_ty_f64()?];
1057 Some(array)
1058 }
1059 _ => None,
1060 }
1061 }
1062}
1063
1064impl<'a> FromKclValue<'a> for [TyF64; 3] {
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() != 3 {
1069 return None;
1070 }
1071 let v0 = value.first()?;
1072 let v1 = value.get(1)?;
1073 let v2 = value.get(2)?;
1074 let array = [v0.as_ty_f64()?, v1.as_ty_f64()?, v2.as_ty_f64()?];
1075 Some(array)
1076 }
1077 _ => None,
1078 }
1079 }
1080}
1081
1082impl<'a> FromKclValue<'a> for Sketch {
1083 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1084 let KclValue::Sketch { value } = arg else {
1085 return None;
1086 };
1087 Some(value.as_ref().to_owned())
1088 }
1089}
1090
1091impl<'a> FromKclValue<'a> for Helix {
1092 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1093 let KclValue::Helix { value } = arg else {
1094 return None;
1095 };
1096 Some(value.as_ref().to_owned())
1097 }
1098}
1099
1100impl<'a> FromKclValue<'a> for SweepPath {
1101 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1102 let case1 = Sketch::from_kcl_val;
1103 let case2 = <Vec<Sketch>>::from_kcl_val;
1104 let case3 = Helix::from_kcl_val;
1105 case1(arg)
1106 .map(Self::Sketch)
1107 .or_else(|| case2(arg).map(|arg0: Vec<Sketch>| Self::Sketch(arg0[0].clone())))
1108 .or_else(|| case3(arg).map(|arg0: Helix| Self::Helix(Box::new(arg0))))
1109 }
1110}
1111impl<'a> FromKclValue<'a> for String {
1112 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1113 let KclValue::String { value, meta: _ } = arg else {
1114 return None;
1115 };
1116 Some(value.to_owned())
1117 }
1118}
1119impl<'a> FromKclValue<'a> for crate::parsing::ast::types::KclNone {
1120 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1121 let KclValue::KclNone { value, meta: _ } = arg else {
1122 return None;
1123 };
1124 Some(value.to_owned())
1125 }
1126}
1127impl<'a> FromKclValue<'a> for bool {
1128 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1129 let KclValue::Bool { value, meta: _ } = arg else {
1130 return None;
1131 };
1132 Some(*value)
1133 }
1134}
1135
1136impl<'a> FromKclValue<'a> for Box<Solid> {
1137 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1138 let KclValue::Solid { value } = arg else {
1139 return None;
1140 };
1141 Some(value.to_owned())
1142 }
1143}
1144
1145impl<'a> FromKclValue<'a> for FunctionSource {
1146 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1147 arg.as_function().cloned()
1148 }
1149}
1150
1151impl<'a> FromKclValue<'a> for SketchOrSurface {
1152 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1153 match arg {
1154 KclValue::Sketch { value: sg } => Some(Self::Sketch(sg.to_owned())),
1155 KclValue::Plane { value } => Some(Self::SketchSurface(SketchSurface::Plane(value.clone()))),
1156 KclValue::Face { value } => Some(Self::SketchSurface(SketchSurface::Face(value.clone()))),
1157 _ => None,
1158 }
1159 }
1160}
1161impl<'a> FromKclValue<'a> for SketchSurface {
1162 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1163 match arg {
1164 KclValue::Plane { value } => Some(Self::Plane(value.clone())),
1165 KclValue::Face { value } => Some(Self::Face(value.clone())),
1166 _ => None,
1167 }
1168 }
1169}
1170
1171impl From<Args> for Metadata {
1172 fn from(value: Args) -> Self {
1173 Self {
1174 source_range: value.source_range,
1175 }
1176 }
1177}
1178
1179impl From<Args> for Vec<Metadata> {
1180 fn from(value: Args) -> Self {
1181 vec![Metadata {
1182 source_range: value.source_range,
1183 }]
1184 }
1185}