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