1use std::{any::type_name, collections::HashMap, num::NonZeroU32};
2
3use anyhow::Result;
4use kcmc::{websocket::OkWebSocketResponseData, ModelingCmd};
5use kittycad_modeling_cmds as kcmc;
6use schemars::JsonSchema;
7use serde::{Deserialize, Serialize};
8
9use super::shapes::PolygonType;
10use crate::{
11 errors::{KclError, KclErrorDetails},
12 execution::{
13 kcl_value::{FunctionSource, NumericType},
14 ExecState, ExecutorContext, ExtrudeSurface, Helix, KclObjectFields, KclValue, Metadata, Sketch, SketchSet,
15 SketchSurface, Solid, SolidSet, TagIdentifier,
16 },
17 parsing::ast::types::TagNode,
18 source_range::SourceRange,
19 std::{shapes::SketchOrSurface, sketch::FaceTag, sweep::SweepPath},
20 ModuleId,
21};
22
23#[derive(Debug, Clone)]
24pub struct Arg {
25 pub value: KclValue,
27 pub source_range: SourceRange,
29}
30
31impl Arg {
32 pub fn new(value: KclValue, source_range: SourceRange) -> Self {
33 Self { value, source_range }
34 }
35
36 pub fn synthetic(value: KclValue) -> Self {
37 Self {
38 value,
39 source_range: SourceRange::synthetic(),
40 }
41 }
42
43 pub fn source_ranges(&self) -> Vec<SourceRange> {
44 vec![self.source_range]
45 }
46}
47
48#[derive(Debug, Clone, Default)]
49pub struct KwArgs {
50 pub unlabeled: Option<Arg>,
52 pub labeled: HashMap<String, Arg>,
54}
55
56impl KwArgs {
57 pub fn len(&self) -> usize {
59 self.labeled.len() + if self.unlabeled.is_some() { 1 } else { 0 }
60 }
61 pub fn is_empty(&self) -> bool {
63 self.labeled.len() == 0 && self.unlabeled.is_none()
64 }
65}
66
67#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
68#[ts(export)]
69#[serde(rename_all = "camelCase")]
70pub struct TyF64 {
71 pub n: f64,
72 pub ty: NumericType,
73}
74
75impl TyF64 {
76 pub fn new(n: f64, ty: NumericType) -> Self {
77 Self { n, ty }
78 }
79
80 pub fn count(n: f64) -> Self {
81 Self {
82 n,
83 ty: NumericType::count(),
84 }
85 }
86
87 pub fn map(mut self, n: f64) -> Self {
88 self.n = n;
89 self
90 }
91}
92
93#[derive(Debug, Clone)]
94pub struct Args {
95 pub args: Vec<Arg>,
97 pub kw_args: KwArgs,
99 pub source_range: SourceRange,
100 pub ctx: ExecutorContext,
101 pipe_value: Option<Arg>,
104}
105
106impl Args {
107 pub fn new(args: Vec<Arg>, source_range: SourceRange, ctx: ExecutorContext, pipe_value: Option<Arg>) -> Self {
108 Self {
109 args,
110 kw_args: Default::default(),
111 source_range,
112 ctx,
113 pipe_value,
114 }
115 }
116
117 pub fn new_kw(kw_args: KwArgs, source_range: SourceRange, ctx: ExecutorContext, pipe_value: Option<Arg>) -> Self {
119 Self {
120 args: Default::default(),
121 kw_args,
122 source_range,
123 ctx,
124 pipe_value,
125 }
126 }
127
128 pub(crate) fn get_kw_arg_opt<'a, T>(&'a self, label: &str) -> Result<Option<T>, KclError>
130 where
131 T: FromKclValue<'a>,
132 {
133 let Some(arg) = self.kw_args.labeled.get(label) else {
134 return Ok(None);
135 };
136
137 T::from_kcl_val(&arg.value).map(Some).ok_or_else(|| {
138 KclError::Type(KclErrorDetails {
139 source_ranges: vec![self.source_range],
140 message: format!(
141 "The optional arg {label} was given, but it was the wrong type. It should be type {} but it was {}",
142 type_name::<T>(),
143 arg.value.human_friendly_type(),
144 ),
145 })
146 })
147 }
148
149 pub(crate) fn get_kw_arg<'a, T>(&'a self, label: &str) -> Result<T, KclError>
151 where
152 T: FromKclValue<'a>,
153 {
154 self.get_kw_arg_opt(label)?.ok_or_else(|| {
155 KclError::Semantic(KclErrorDetails {
156 source_ranges: vec![self.source_range],
157 message: format!("This function requires a keyword argument '{label}'"),
158 })
159 })
160 }
161
162 pub(crate) fn kw_arg_array_and_source<'a, T>(&'a self, label: &str) -> Result<Vec<(T, SourceRange)>, KclError>
165 where
166 T: FromKclValue<'a>,
167 {
168 let Some(arg) = self.kw_args.labeled.get(label) else {
169 let err = KclError::Semantic(KclErrorDetails {
170 source_ranges: vec![self.source_range],
171 message: format!("This function requires a keyword argument '{label}'"),
172 });
173 return Err(err);
174 };
175 let Some(array) = arg.value.as_array() else {
176 let err = KclError::Semantic(KclErrorDetails {
177 source_ranges: vec![arg.source_range],
178 message: format!(
179 "Expected an array of {} but found {}",
180 type_name::<T>(),
181 arg.value.human_friendly_type()
182 ),
183 });
184 return Err(err);
185 };
186 array
187 .iter()
188 .map(|item| {
189 let source = SourceRange::from(item);
190 let val = FromKclValue::from_kcl_val(item).ok_or_else(|| {
191 KclError::Semantic(KclErrorDetails {
192 source_ranges: arg.source_ranges(),
193 message: format!(
194 "Expected a {} but found {}",
195 type_name::<T>(),
196 arg.value.human_friendly_type()
197 ),
198 })
199 })?;
200 Ok((val, source))
201 })
202 .collect::<Result<Vec<_>, _>>()
203 }
204
205 pub(crate) fn unlabeled_kw_arg_unconverted(&self) -> Option<&Arg> {
207 self.kw_args
208 .unlabeled
209 .as_ref()
210 .or(self.args.first())
211 .or(self.pipe_value.as_ref())
212 }
213
214 pub(crate) fn get_unlabeled_kw_arg<'a, T>(&'a self, label: &str) -> Result<T, KclError>
217 where
218 T: FromKclValue<'a>,
219 {
220 let arg = self
221 .unlabeled_kw_arg_unconverted()
222 .ok_or(KclError::Semantic(KclErrorDetails {
223 source_ranges: vec![self.source_range],
224 message: format!("This function requires a value for the special unlabeled first parameter, '{label}'"),
225 }))?;
226
227 T::from_kcl_val(&arg.value).ok_or_else(|| {
228 let expected_type_name = tynm::type_name::<T>();
229 let actual_type_name = arg.value.human_friendly_type();
230 let msg_base = format!("This function expected this argument to be of type {expected_type_name} but it's actually of type {actual_type_name}");
231 let suggestion = match (expected_type_name.as_str(), actual_type_name) {
232 ("SolidSet", "Sketch") => Some(
233 "You can convert a sketch (2D) into a Solid (3D) by calling a function like `extrude` or `revolve`",
234 ),
235 _ => None,
236 };
237 let message = match suggestion {
238 None => msg_base,
239 Some(sugg) => format!("{msg_base}. {sugg}"),
240 };
241 KclError::Semantic(KclErrorDetails {
242 source_ranges: arg.source_ranges(),
243 message,
244 })
245 })
246 }
247
248 pub(crate) async fn batch_modeling_cmd(
250 &self,
251 id: uuid::Uuid,
252 cmd: ModelingCmd,
253 ) -> Result<(), crate::errors::KclError> {
254 self.ctx.engine.batch_modeling_cmd(id, self.source_range, &cmd).await
255 }
256
257 pub(crate) async fn batch_end_cmd(&self, id: uuid::Uuid, cmd: ModelingCmd) -> Result<(), crate::errors::KclError> {
261 self.ctx.engine.batch_end_cmd(id, self.source_range, &cmd).await
262 }
263
264 pub(crate) async fn send_modeling_cmd(
266 &self,
267 id: uuid::Uuid,
268 cmd: ModelingCmd,
269 ) -> Result<OkWebSocketResponseData, KclError> {
270 self.ctx.engine.send_modeling_cmd(id, self.source_range, &cmd).await
271 }
272
273 fn get_tag_info_from_memory<'a, 'e>(
274 &'a self,
275 exec_state: &'e mut ExecState,
276 tag: &'a TagIdentifier,
277 ) -> Result<&'e crate::execution::TagEngineInfo, KclError> {
278 if let KclValue::TagIdentifier(t) = exec_state.stack().get_from_call_stack(&tag.value, self.source_range)? {
279 Ok(t.info.as_ref().ok_or_else(|| {
280 KclError::Type(KclErrorDetails {
281 message: format!("Tag `{}` does not have engine info", tag.value),
282 source_ranges: vec![self.source_range],
283 })
284 })?)
285 } else {
286 Err(KclError::Type(KclErrorDetails {
287 message: format!("Tag `{}` does not exist", tag.value),
288 source_ranges: vec![self.source_range],
289 }))
290 }
291 }
292
293 pub(crate) fn get_tag_engine_info<'a, 'e>(
294 &'a self,
295 exec_state: &'e mut ExecState,
296 tag: &'a TagIdentifier,
297 ) -> Result<&'a crate::execution::TagEngineInfo, KclError>
298 where
299 'e: 'a,
300 {
301 if let Some(info) = &tag.info {
302 return Ok(info);
303 }
304
305 self.get_tag_info_from_memory(exec_state, tag)
306 }
307
308 fn get_tag_engine_info_check_surface<'a, 'e>(
309 &'a self,
310 exec_state: &'e mut ExecState,
311 tag: &'a TagIdentifier,
312 ) -> Result<&'a crate::execution::TagEngineInfo, KclError>
313 where
314 'e: 'a,
315 {
316 if let Some(info) = &tag.info {
317 if info.surface.is_some() {
318 return Ok(info);
319 }
320 }
321
322 self.get_tag_info_from_memory(exec_state, tag)
323 }
324
325 #[allow(clippy::vec_box)]
327 pub(crate) async fn flush_batch_for_solid_set(
328 &self,
329 exec_state: &mut ExecState,
330 solids: Vec<Box<Solid>>,
331 ) -> Result<(), KclError> {
332 let mut traversed_sketches = Vec::new();
334
335 let mut ids = Vec::new();
337 for solid in solids {
338 let sketch_id = solid.sketch.id;
340 if !traversed_sketches.contains(&sketch_id) {
341 ids.extend(
343 exec_state
344 .stack()
345 .walk_call_stack()
346 .filter(|v| matches!(v, KclValue::Solid { value } if value.sketch.id == sketch_id))
347 .flat_map(|v| match v {
348 KclValue::Solid { value } => value.get_all_edge_cut_ids(),
349 _ => unreachable!(),
350 }),
351 );
352 traversed_sketches.push(sketch_id);
353 }
354
355 ids.extend(solid.get_all_edge_cut_ids());
356 }
357
358 if ids.is_empty() {
360 return Ok(());
361 }
362
363 for id in ids {
366 let Some(item) = self.ctx.engine.batch_end().write().await.shift_remove(&id) else {
368 continue;
370 };
371 self.ctx.engine.batch().write().await.push(item);
373 }
374
375 self.ctx.engine.flush_batch(false, SourceRange::default()).await?;
378
379 Ok(())
380 }
381
382 pub(crate) fn make_user_val_from_point(&self, p: [f64; 2]) -> Result<KclValue, KclError> {
383 let meta = Metadata {
384 source_range: self.source_range,
385 };
386 let x = KclValue::Number {
387 value: p[0],
388 meta: vec![meta],
389 ty: NumericType::Unknown,
390 };
391 let y = KclValue::Number {
392 value: p[1],
393 meta: vec![meta],
394 ty: NumericType::Unknown,
395 };
396 Ok(KclValue::MixedArray {
397 value: vec![x, y],
398 meta: vec![meta],
399 })
400 }
401
402 pub(crate) fn make_user_val_from_f64(&self, f: f64) -> KclValue {
403 KclValue::from_number(
404 f,
405 vec![Metadata {
406 source_range: self.source_range,
407 }],
408 )
409 }
410
411 pub(crate) fn make_user_val_from_f64_with_type(&self, f: TyF64) -> KclValue {
412 KclValue::from_number_with_type(
413 f.n,
414 f.ty,
415 vec![Metadata {
416 source_range: self.source_range,
417 }],
418 )
419 }
420
421 pub(crate) fn make_user_val_from_f64_array(&self, f: Vec<f64>, ty: &NumericType) -> Result<KclValue, KclError> {
422 let array = f
423 .into_iter()
424 .map(|n| KclValue::Number {
425 value: n,
426 meta: vec![Metadata {
427 source_range: self.source_range,
428 }],
429 ty: ty.clone(),
430 })
431 .collect::<Vec<_>>();
432 Ok(KclValue::MixedArray {
433 value: array,
434 meta: vec![Metadata {
435 source_range: self.source_range,
436 }],
437 })
438 }
439
440 pub(crate) fn get_number(&self) -> Result<f64, KclError> {
441 FromArgs::from_args(self, 0)
442 }
443
444 pub(crate) fn get_number_with_type(&self) -> Result<TyF64, KclError> {
445 FromArgs::from_args(self, 0)
446 }
447
448 pub(crate) fn get_number_array(&self) -> Result<Vec<f64>, KclError> {
449 let numbers = self
450 .args
451 .iter()
452 .map(|arg| {
453 let Some(num) = f64::from_kcl_val(&arg.value) else {
454 return Err(KclError::Semantic(KclErrorDetails {
455 source_ranges: arg.source_ranges(),
456 message: format!("Expected a number but found {}", arg.value.human_friendly_type()),
457 }));
458 };
459 Ok(num)
460 })
461 .collect::<Result<_, _>>()?;
462 Ok(numbers)
463 }
464
465 pub(crate) fn get_number_array_with_types(&self) -> Result<Vec<TyF64>, KclError> {
466 let numbers = self
467 .args
468 .iter()
469 .map(|arg| {
470 let Some(num) = <TyF64>::from_kcl_val(&arg.value) else {
471 return Err(KclError::Semantic(KclErrorDetails {
472 source_ranges: arg.source_ranges(),
473 message: format!("Expected a number but found {}", arg.value.human_friendly_type()),
474 }));
475 };
476 Ok(num)
477 })
478 .collect::<Result<_, _>>()?;
479 Ok(numbers)
480 }
481
482 pub(crate) fn get_hypotenuse_leg(&self) -> Result<(f64, f64, NumericType), KclError> {
483 let numbers = self.get_number_array_with_types()?;
484
485 if numbers.len() != 2 {
486 return Err(KclError::Type(KclErrorDetails {
487 message: format!("Expected a number array of length 2, found `{:?}`", numbers),
488 source_ranges: vec![self.source_range],
489 }));
490 }
491
492 let mut numbers = numbers.into_iter();
493 let a = numbers.next().unwrap();
494 let b = numbers.next().unwrap();
495 let ty = a.ty.combine_eq(&b.ty);
496 Ok((a.n, b.n, ty))
497 }
498
499 pub(crate) fn get_sketches(&self) -> Result<(SketchSet, Sketch), KclError> {
500 FromArgs::from_args(self, 0)
501 }
502
503 pub(crate) fn get_sketch(&self) -> Result<Sketch, KclError> {
504 FromArgs::from_args(self, 0)
505 }
506
507 pub(crate) fn get_data<'a, T>(&'a self) -> Result<T, KclError>
508 where
509 T: FromArgs<'a> + serde::de::DeserializeOwned,
510 {
511 FromArgs::from_args(self, 0)
512 }
513
514 pub(crate) fn get_import_data(&self) -> Result<(String, Option<crate::std::import::ImportFormat>), KclError> {
515 FromArgs::from_args(self, 0)
516 }
517
518 pub(crate) fn get_data_and_optional_tag<'a, T>(&'a self) -> Result<(T, Option<FaceTag>), KclError>
519 where
520 T: serde::de::DeserializeOwned + FromKclValue<'a> + Sized,
521 {
522 FromArgs::from_args(self, 0)
523 }
524
525 pub(crate) fn get_data_and_sketch<'a, T>(&'a self) -> Result<(T, Sketch), KclError>
526 where
527 T: serde::de::DeserializeOwned + FromArgs<'a>,
528 {
529 FromArgs::from_args(self, 0)
530 }
531
532 pub(crate) fn get_data_and_sketch_set<'a, T>(&'a self) -> Result<(T, SketchSet), KclError>
533 where
534 T: serde::de::DeserializeOwned + FromArgs<'a>,
535 {
536 FromArgs::from_args(self, 0)
537 }
538
539 pub(crate) fn get_data_and_sketch_and_tag<'a, T>(&'a self) -> Result<(T, Sketch, Option<TagNode>), KclError>
540 where
541 T: serde::de::DeserializeOwned + FromKclValue<'a> + Sized,
542 {
543 FromArgs::from_args(self, 0)
544 }
545
546 pub(crate) fn get_data_and_sketch_surface<'a, T>(&'a self) -> Result<(T, SketchSurface, Option<TagNode>), KclError>
547 where
548 T: serde::de::DeserializeOwned + FromKclValue<'a> + Sized,
549 {
550 FromArgs::from_args(self, 0)
551 }
552
553 pub(crate) fn get_data_and_solid<'a, T>(&'a self) -> Result<(T, Box<Solid>), KclError>
554 where
555 T: serde::de::DeserializeOwned + FromKclValue<'a> + Sized,
556 {
557 FromArgs::from_args(self, 0)
558 }
559
560 pub(crate) fn get_tag_to_number_sketch(&self) -> Result<(TagIdentifier, f64, Sketch), KclError> {
561 FromArgs::from_args(self, 0)
562 }
563
564 pub(crate) async fn get_adjacent_face_to_tag(
565 &self,
566 exec_state: &mut ExecState,
567 tag: &TagIdentifier,
568 must_be_planar: bool,
569 ) -> Result<uuid::Uuid, KclError> {
570 if tag.value.is_empty() {
571 return Err(KclError::Type(KclErrorDetails {
572 message: "Expected a non-empty tag for the face".to_string(),
573 source_ranges: vec![self.source_range],
574 }));
575 }
576
577 let engine_info = self.get_tag_engine_info_check_surface(exec_state, tag)?;
578
579 let surface = engine_info.surface.as_ref().ok_or_else(|| {
580 KclError::Type(KclErrorDetails {
581 message: format!("Tag `{}` does not have a surface", tag.value),
582 source_ranges: vec![self.source_range],
583 })
584 })?;
585
586 if let Some(face_from_surface) = match surface {
587 ExtrudeSurface::ExtrudePlane(extrude_plane) => {
588 if let Some(plane_tag) = &extrude_plane.tag {
589 if plane_tag.name == tag.value {
590 Some(Ok(extrude_plane.face_id))
591 } else {
592 None
593 }
594 } else {
595 None
596 }
597 }
598 ExtrudeSurface::ExtrudeArc(_) if must_be_planar => Some(Err(KclError::Type(KclErrorDetails {
600 message: format!("Tag `{}` is a non-planar surface", tag.value),
601 source_ranges: vec![self.source_range],
602 }))),
603 ExtrudeSurface::ExtrudeArc(extrude_arc) => {
604 if let Some(arc_tag) = &extrude_arc.tag {
605 if arc_tag.name == tag.value {
606 Some(Ok(extrude_arc.face_id))
607 } else {
608 None
609 }
610 } else {
611 None
612 }
613 }
614 ExtrudeSurface::Chamfer(chamfer) => {
615 if let Some(chamfer_tag) = &chamfer.tag {
616 if chamfer_tag.name == tag.value {
617 Some(Ok(chamfer.face_id))
618 } else {
619 None
620 }
621 } else {
622 None
623 }
624 }
625 ExtrudeSurface::Fillet(_) if must_be_planar => Some(Err(KclError::Type(KclErrorDetails {
627 message: format!("Tag `{}` is a non-planar surface", tag.value),
628 source_ranges: vec![self.source_range],
629 }))),
630 ExtrudeSurface::Fillet(fillet) => {
631 if let Some(fillet_tag) = &fillet.tag {
632 if fillet_tag.name == tag.value {
633 Some(Ok(fillet.face_id))
634 } else {
635 None
636 }
637 } else {
638 None
639 }
640 }
641 } {
642 return face_from_surface;
643 }
644
645 Err(KclError::Type(KclErrorDetails {
647 message: format!("Expected a face with the tag `{}`", tag.value),
648 source_ranges: vec![self.source_range],
649 }))
650 }
651
652 pub(crate) fn get_polygon_args(
653 &self,
654 ) -> Result<
655 (
656 crate::std::shapes::PolygonData,
657 crate::std::shapes::SketchOrSurface,
658 Option<TagNode>,
659 ),
660 KclError,
661 > {
662 FromArgs::from_args(self, 0)
663 }
664}
665
666pub trait FromArgs<'a>: Sized {
668 fn from_args(args: &'a Args, index: usize) -> Result<Self, KclError>;
670}
671
672pub trait FromKclValue<'a>: Sized {
674 fn from_kcl_val(arg: &'a KclValue) -> Option<Self>;
676}
677
678impl<'a> FromArgs<'a> for Vec<KclValue> {
679 fn from_args(args: &'a Args, i: usize) -> Result<Self, KclError> {
680 let Some(arg) = args.args.get(i) else {
681 return Err(KclError::Semantic(KclErrorDetails {
682 message: format!("Expected an argument at index {i}"),
683 source_ranges: vec![args.source_range],
684 }));
685 };
686 let KclValue::MixedArray { value: array, meta: _ } = &arg.value else {
687 let message = format!("Expected an array but found {}", arg.value.human_friendly_type());
688 return Err(KclError::Type(KclErrorDetails {
689 source_ranges: arg.source_ranges(),
690 message,
691 }));
692 };
693 Ok(array.to_owned())
694 }
695}
696
697impl<'a, T> FromArgs<'a> for T
698where
699 T: FromKclValue<'a> + Sized,
700{
701 fn from_args(args: &'a Args, i: usize) -> Result<Self, KclError> {
702 let Some(arg) = args.args.get(i) else {
703 return Err(KclError::Semantic(KclErrorDetails {
704 message: format!("Expected an argument at index {i}"),
705 source_ranges: vec![args.source_range],
706 }));
707 };
708 let Some(val) = T::from_kcl_val(&arg.value) else {
709 return Err(KclError::Semantic(KclErrorDetails {
710 message: format!(
711 "Argument at index {i} was supposed to be type {} but found {}",
712 type_name::<T>(),
713 arg.value.human_friendly_type(),
714 ),
715 source_ranges: arg.source_ranges(),
716 }));
717 };
718 Ok(val)
719 }
720}
721
722impl<'a, T> FromArgs<'a> for Option<T>
723where
724 T: FromKclValue<'a> + Sized,
725{
726 fn from_args(args: &'a Args, i: usize) -> Result<Self, KclError> {
727 let Some(arg) = args.args.get(i) else { return Ok(None) };
728 if crate::parsing::ast::types::KclNone::from_kcl_val(&arg.value).is_some() {
729 return Ok(None);
730 }
731 let Some(val) = T::from_kcl_val(&arg.value) else {
732 return Err(KclError::Semantic(KclErrorDetails {
733 message: format!(
734 "Argument at index {i} was supposed to be type Option<{}> but found {}",
735 type_name::<T>(),
736 arg.value.human_friendly_type()
737 ),
738 source_ranges: arg.source_ranges(),
739 }));
740 };
741 Ok(Some(val))
742 }
743}
744
745impl<'a, A, B> FromArgs<'a> for (A, B)
746where
747 A: FromArgs<'a>,
748 B: FromArgs<'a>,
749{
750 fn from_args(args: &'a Args, i: usize) -> Result<Self, KclError> {
751 let a = A::from_args(args, i)?;
752 let b = B::from_args(args, i + 1)?;
753 Ok((a, b))
754 }
755}
756
757impl<'a, A, B, C> FromArgs<'a> for (A, B, C)
758where
759 A: FromArgs<'a>,
760 B: FromArgs<'a>,
761 C: FromArgs<'a>,
762{
763 fn from_args(args: &'a Args, i: usize) -> Result<Self, KclError> {
764 let a = A::from_args(args, i)?;
765 let b = B::from_args(args, i + 1)?;
766 let c = C::from_args(args, i + 2)?;
767 Ok((a, b, c))
768 }
769}
770impl<'a, A, B, C, D> FromArgs<'a> for (A, B, C, D)
771where
772 A: FromArgs<'a>,
773 B: FromArgs<'a>,
774 C: FromArgs<'a>,
775 D: FromArgs<'a>,
776{
777 fn from_args(args: &'a Args, i: usize) -> Result<Self, KclError> {
778 let a = A::from_args(args, i)?;
779 let b = B::from_args(args, i + 1)?;
780 let c = C::from_args(args, i + 2)?;
781 let d = D::from_args(args, i + 3)?;
782 Ok((a, b, c, d))
783 }
784}
785
786impl<'a> FromKclValue<'a> for [f64; 2] {
787 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
788 let KclValue::MixedArray { value, meta: _ } = arg else {
789 return None;
790 };
791 if value.len() != 2 {
792 return None;
793 }
794 let v0 = value.first()?;
795 let v1 = value.get(1)?;
796 let array = [v0.as_f64()?, v1.as_f64()?];
797 Some(array)
798 }
799}
800
801impl<'a> FromKclValue<'a> for [usize; 3] {
802 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
803 let KclValue::MixedArray { value, meta: _ } = arg else {
804 return None;
805 };
806 if value.len() != 3 {
807 return None;
808 }
809 let v0 = value.first()?;
810 let v1 = value.get(1)?;
811 let v2 = value.get(2)?;
812 let array = [v0.as_usize()?, v1.as_usize()?, v2.as_usize()?];
813 Some(array)
814 }
815}
816
817impl<'a> FromKclValue<'a> for [f64; 3] {
818 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
819 let KclValue::MixedArray { value, meta: _ } = arg else {
820 return None;
821 };
822 if value.len() != 3 {
823 return None;
824 }
825 let v0 = value.first()?;
826 let v1 = value.get(1)?;
827 let v2 = value.get(2)?;
828 let array = [v0.as_f64()?, v1.as_f64()?, v2.as_f64()?];
829 Some(array)
830 }
831}
832
833impl<'a> FromKclValue<'a> for TagNode {
834 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
835 arg.get_tag_declarator().ok()
836 }
837}
838
839impl<'a> FromKclValue<'a> for TagIdentifier {
840 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
841 arg.get_tag_identifier().ok()
842 }
843}
844
845impl<'a> FromKclValue<'a> for KclValue {
846 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
847 Some(arg.clone())
848 }
849}
850
851macro_rules! let_field_of {
852 ($obj:ident, $field:ident?) => {
854 let $field = $obj.get(stringify!($field)).and_then(FromKclValue::from_kcl_val);
855 };
856 ($obj:ident, $field:ident? $key:literal) => {
858 let $field = $obj.get($key).and_then(FromKclValue::from_kcl_val);
859 };
860 ($obj:ident, $field:ident $key:literal) => {
862 let $field = $obj.get($key).and_then(FromKclValue::from_kcl_val)?;
863 };
864 ($obj:ident, $field:ident $(, $annotation:ty)?) => {
866 let $field $(: $annotation)? = $obj.get(stringify!($field)).and_then(FromKclValue::from_kcl_val)?;
867 };
868}
869
870impl<'a> FromKclValue<'a> for crate::std::import::ImportFormat {
871 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
872 let obj = arg.as_object()?;
873 let_field_of!(obj, typ "format");
874 match typ {
875 "fbx" => Some(Self::Fbx {}),
876 "gltf" => Some(Self::Gltf {}),
877 "sldprt" => Some(Self::Sldprt {}),
878 "step" => Some(Self::Step {}),
879 "stl" => {
880 let_field_of!(obj, coords?);
881 let_field_of!(obj, units);
882 Some(Self::Stl { coords, units })
883 }
884 "obj" => {
885 let_field_of!(obj, coords?);
886 let_field_of!(obj, units);
887 Some(Self::Obj { coords, units })
888 }
889 "ply" => {
890 let_field_of!(obj, coords?);
891 let_field_of!(obj, units);
892 Some(Self::Ply { coords, units })
893 }
894 _ => None,
895 }
896 }
897}
898
899impl<'a> FromKclValue<'a> for super::sketch::AngledLineThatIntersectsData {
900 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
901 let obj = arg.as_object()?;
902 let_field_of!(obj, angle);
903 let_field_of!(obj, intersect_tag "intersectTag");
904 let_field_of!(obj, offset?);
905 Some(Self {
906 angle,
907 intersect_tag,
908 offset,
909 })
910 }
911}
912
913impl<'a> FromKclValue<'a> for super::shapes::PolygonData {
914 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
915 let obj = arg.as_object()?;
916 let_field_of!(obj, radius);
917 let_field_of!(obj, num_sides "numSides");
918 let_field_of!(obj, center);
919 let_field_of!(obj, inscribed);
920 let polygon_type = if inscribed {
921 PolygonType::Inscribed
922 } else {
923 PolygonType::Circumscribed
924 };
925 Some(Self {
926 radius,
927 num_sides,
928 center,
929 polygon_type,
930 inscribed,
931 })
932 }
933}
934
935impl<'a> FromKclValue<'a> for crate::std::polar::PolarCoordsData {
936 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
937 let obj = arg.as_object()?;
938 let_field_of!(obj, angle);
939 let_field_of!(obj, length);
940 Some(Self { angle, length })
941 }
942}
943
944impl<'a> FromKclValue<'a> for crate::execution::Plane {
945 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
946 arg.as_plane().cloned()
947 }
948}
949
950impl<'a> FromKclValue<'a> for crate::execution::PlaneType {
951 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
952 let plane_type = match arg.as_str()? {
953 "XY" | "xy" => Self::XY,
954 "XZ" | "xz" => Self::XZ,
955 "YZ" | "yz" => Self::YZ,
956 "Custom" => Self::Custom,
957 _ => return None,
958 };
959 Some(plane_type)
960 }
961}
962
963impl<'a> FromKclValue<'a> for kittycad_modeling_cmds::units::UnitLength {
964 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
965 let s = arg.as_str()?;
966 s.parse().ok()
967 }
968}
969
970impl<'a> FromKclValue<'a> for kittycad_modeling_cmds::coord::System {
971 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
972 let obj = arg.as_object()?;
973 let_field_of!(obj, forward);
974 let_field_of!(obj, up);
975 Some(Self { forward, up })
976 }
977}
978
979impl<'a> FromKclValue<'a> for kittycad_modeling_cmds::coord::AxisDirectionPair {
980 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
981 let obj = arg.as_object()?;
982 let_field_of!(obj, axis);
983 let_field_of!(obj, direction);
984 Some(Self { axis, direction })
985 }
986}
987
988impl<'a> FromKclValue<'a> for kittycad_modeling_cmds::coord::Axis {
989 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
990 let s = arg.as_str()?;
991 match s {
992 "y" => Some(Self::Y),
993 "z" => Some(Self::Z),
994 _ => None,
995 }
996 }
997}
998
999impl<'a> FromKclValue<'a> for PolygonType {
1000 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1001 let s = arg.as_str()?;
1002 match s {
1003 "inscribed" => Some(Self::Inscribed),
1004 _ => Some(Self::Circumscribed),
1005 }
1006 }
1007}
1008
1009impl<'a> FromKclValue<'a> for kittycad_modeling_cmds::coord::Direction {
1010 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1011 let s = arg.as_str()?;
1012 match s {
1013 "positive" => Some(Self::Positive),
1014 "negative" => Some(Self::Negative),
1015 _ => None,
1016 }
1017 }
1018}
1019
1020impl<'a> FromKclValue<'a> for super::sketch::BezierData {
1021 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1022 let obj = arg.as_object()?;
1023 let_field_of!(obj, to);
1024 let_field_of!(obj, control1);
1025 let_field_of!(obj, control2);
1026 Some(Self { to, control1, control2 })
1027 }
1028}
1029
1030impl<'a> FromKclValue<'a> for super::helix::HelixRevolutionsData {
1031 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1032 let obj = arg.as_object()?;
1033 let_field_of!(obj, revolutions);
1034 let_field_of!(obj, length?);
1035 let_field_of!(obj, ccw?);
1036 let ccw = ccw.unwrap_or_default();
1037 let angle_start = obj.get("angleStart")?.as_f64()?;
1038 Some(Self {
1039 revolutions,
1040 angle_start,
1041 ccw,
1042 length,
1043 })
1044 }
1045}
1046
1047impl<'a> FromKclValue<'a> for FaceTag {
1048 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1049 let case1 = || match arg.as_str() {
1050 Some("start" | "START") => Some(Self::StartOrEnd(super::sketch::StartOrEnd::Start)),
1051 Some("end" | "END") => Some(Self::StartOrEnd(super::sketch::StartOrEnd::End)),
1052 _ => None,
1053 };
1054 let case2 = || {
1055 let tag = TagIdentifier::from_kcl_val(arg)?;
1056 Some(Self::Tag(Box::new(tag)))
1057 };
1058 case1().or_else(case2)
1059 }
1060}
1061
1062impl<'a> FromKclValue<'a> for super::sketch::AngledLineToData {
1063 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1064 let case1 = || {
1066 let obj = arg.as_object()?;
1067 let_field_of!(obj, to);
1068 let_field_of!(obj, angle);
1069 Some(Self { angle, to })
1070 };
1071 let case2 = || {
1073 let [angle, to] = arg.as_point2d()?;
1074 Some(Self { angle, to })
1075 };
1076 case1().or_else(case2)
1077 }
1078}
1079
1080impl<'a> FromKclValue<'a> for super::sketch::ArcData {
1081 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1082 let obj = arg.as_object()?;
1083 let_field_of!(obj, radius);
1084 let case1 = || {
1085 let angle_start = obj.get("angleStart")?.as_f64()?;
1086 let angle_end = obj.get("angleEnd")?.as_f64()?;
1087 Some(Self::AnglesAndRadius {
1088 angle_start,
1089 angle_end,
1090 radius,
1091 })
1092 };
1093 let case2 = || {
1094 let obj = arg.as_object()?;
1095 let_field_of!(obj, to);
1096 let_field_of!(obj, center);
1097 Some(Self::CenterToRadius { center, to, radius })
1098 };
1099 case1().or_else(case2)
1100 }
1101}
1102
1103impl<'a> FromKclValue<'a> for super::sketch::ArcToData {
1104 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1105 let obj = arg.as_object()?;
1106 let_field_of!(obj, end);
1107 let_field_of!(obj, interior);
1108 Some(Self { end, interior })
1109 }
1110}
1111
1112impl<'a> FromKclValue<'a> for super::revolve::RevolveData {
1113 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1114 let obj = arg.as_object()?;
1115 let angle = obj.get("angle").and_then(|x| x.as_f64());
1116 let tolerance = obj.get("tolerance").and_then(|x| x.as_f64());
1117 let_field_of!(obj, axis);
1118 Some(Self { angle, axis, tolerance })
1119 }
1120}
1121
1122impl<'a> FromKclValue<'a> for super::sketch::TangentialArcData {
1123 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1124 let obj = arg.as_object()?;
1125 let_field_of!(obj, radius);
1126 let_field_of!(obj, offset);
1127 Some(Self::RadiusAndOffset { radius, offset })
1128 }
1129}
1130
1131impl<'a> FromKclValue<'a> for crate::execution::Point3d {
1132 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1133 if let Some(obj) = arg.as_object() {
1135 let_field_of!(obj, x);
1136 let_field_of!(obj, y);
1137 let_field_of!(obj, z);
1138 return Some(Self { x, y, z });
1139 }
1140 let [x, y, z]: [f64; 3] = FromKclValue::from_kcl_val(arg)?;
1142 Some(Self { x, y, z })
1143 }
1144}
1145
1146impl<'a> FromKclValue<'a> for super::sketch::PlaneData {
1147 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1148 if let KclValue::Plane { value } = arg {
1150 return Some(Self::Plane {
1151 origin: value.origin,
1152 x_axis: value.x_axis,
1153 y_axis: value.y_axis,
1154 z_axis: value.z_axis,
1155 });
1156 }
1157 if let Some(s) = arg.as_str() {
1159 return match s {
1160 "XY" | "xy" => Some(Self::XY),
1161 "-XY" | "-xy" => Some(Self::NegXY),
1162 "XZ" | "xz" => Some(Self::XZ),
1163 "-XZ" | "-xz" => Some(Self::NegXZ),
1164 "YZ" | "yz" => Some(Self::YZ),
1165 "-YZ" | "-yz" => Some(Self::NegYZ),
1166 _ => None,
1167 };
1168 }
1169 let obj = arg.as_object()?;
1171 let_field_of!(obj, plane, &KclObjectFields);
1172 let origin = plane.get("origin").and_then(FromKclValue::from_kcl_val)?;
1173 let x_axis = plane.get("xAxis").and_then(FromKclValue::from_kcl_val)?;
1174 let y_axis = plane.get("yAxis").and_then(FromKclValue::from_kcl_val)?;
1175 let z_axis = plane.get("zAxis").and_then(FromKclValue::from_kcl_val)?;
1176 Some(Self::Plane {
1177 origin,
1178 x_axis,
1179 y_axis,
1180 z_axis,
1181 })
1182 }
1183}
1184
1185impl<'a> FromKclValue<'a> for crate::execution::ExtrudePlane {
1186 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1187 let obj = arg.as_object()?;
1188 let_field_of!(obj, face_id "faceId");
1189 let tag = FromKclValue::from_kcl_val(obj.get("tag")?);
1190 let_field_of!(obj, geo_meta "geoMeta");
1191 Some(Self { face_id, tag, geo_meta })
1192 }
1193}
1194
1195impl<'a> FromKclValue<'a> for crate::execution::ExtrudeArc {
1196 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1197 let obj = arg.as_object()?;
1198 let_field_of!(obj, face_id "faceId");
1199 let tag = FromKclValue::from_kcl_val(obj.get("tag")?);
1200 let_field_of!(obj, geo_meta "geoMeta");
1201 Some(Self { face_id, tag, geo_meta })
1202 }
1203}
1204
1205impl<'a> FromKclValue<'a> for crate::execution::GeoMeta {
1206 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1207 let obj = arg.as_object()?;
1208 let_field_of!(obj, id);
1209 let_field_of!(obj, source_range "sourceRange");
1210 Some(Self {
1211 id,
1212 metadata: Metadata { source_range },
1213 })
1214 }
1215}
1216
1217impl<'a> FromKclValue<'a> for crate::execution::ChamferSurface {
1218 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1219 let obj = arg.as_object()?;
1220 let_field_of!(obj, face_id "faceId");
1221 let tag = FromKclValue::from_kcl_val(obj.get("tag")?);
1222 let_field_of!(obj, geo_meta "geoMeta");
1223 Some(Self { face_id, tag, geo_meta })
1224 }
1225}
1226
1227impl<'a> FromKclValue<'a> for crate::execution::FilletSurface {
1228 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1229 let obj = arg.as_object()?;
1230 let_field_of!(obj, face_id "faceId");
1231 let tag = FromKclValue::from_kcl_val(obj.get("tag")?);
1232 let_field_of!(obj, geo_meta "geoMeta");
1233 Some(Self { face_id, tag, geo_meta })
1234 }
1235}
1236
1237impl<'a> FromKclValue<'a> for ExtrudeSurface {
1238 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1239 let case1 = crate::execution::ExtrudePlane::from_kcl_val;
1240 let case2 = crate::execution::ExtrudeArc::from_kcl_val;
1241 let case3 = crate::execution::ChamferSurface::from_kcl_val;
1242 let case4 = crate::execution::FilletSurface::from_kcl_val;
1243 case1(arg)
1244 .map(Self::ExtrudePlane)
1245 .or_else(|| case2(arg).map(Self::ExtrudeArc))
1246 .or_else(|| case3(arg).map(Self::Chamfer))
1247 .or_else(|| case4(arg).map(Self::Fillet))
1248 }
1249}
1250
1251impl<'a> FromKclValue<'a> for crate::execution::EdgeCut {
1252 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1253 let obj = arg.as_object()?;
1254 let_field_of!(obj, typ "type");
1255 let tag = Box::new(obj.get("tag").and_then(FromKclValue::from_kcl_val));
1256 let_field_of!(obj, edge_id "edgeId");
1257 let_field_of!(obj, id);
1258 match typ {
1259 "fillet" => {
1260 let_field_of!(obj, radius);
1261 Some(Self::Fillet {
1262 edge_id,
1263 tag,
1264 id,
1265 radius,
1266 })
1267 }
1268 "chamfer" => {
1269 let_field_of!(obj, length);
1270 Some(Self::Chamfer {
1271 id,
1272 length,
1273 edge_id,
1274 tag,
1275 })
1276 }
1277 _ => None,
1278 }
1279 }
1280}
1281
1282macro_rules! impl_from_kcl_for_vec {
1283 ($typ:path) => {
1284 impl<'a> FromKclValue<'a> for Vec<$typ> {
1285 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1286 arg.as_array()?
1287 .iter()
1288 .map(|value| FromKclValue::from_kcl_val(value))
1289 .collect::<Option<_>>()
1290 }
1291 }
1292 };
1293}
1294
1295impl_from_kcl_for_vec!(FaceTag);
1296impl_from_kcl_for_vec!(crate::execution::EdgeCut);
1297impl_from_kcl_for_vec!(crate::execution::Metadata);
1298impl_from_kcl_for_vec!(super::fillet::EdgeReference);
1299impl_from_kcl_for_vec!(ExtrudeSurface);
1300impl_from_kcl_for_vec!(Sketch);
1301
1302impl<'a> FromKclValue<'a> for SourceRange {
1303 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1304 let KclValue::MixedArray { value, meta: _ } = arg else {
1305 return None;
1306 };
1307 if value.len() != 3 {
1308 return None;
1309 }
1310 let v0 = value.first()?;
1311 let v1 = value.get(1)?;
1312 let v2 = value.get(2)?;
1313 Some(SourceRange::new(
1314 v0.as_usize()?,
1315 v1.as_usize()?,
1316 ModuleId::from_usize(v2.as_usize()?),
1317 ))
1318 }
1319}
1320
1321impl<'a> FromKclValue<'a> for crate::execution::Metadata {
1322 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1323 FromKclValue::from_kcl_val(arg).map(|sr| Self { source_range: sr })
1324 }
1325}
1326
1327impl<'a> FromKclValue<'a> for crate::execution::Solid {
1328 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1329 arg.as_solid().cloned()
1330 }
1331}
1332
1333impl<'a> FromKclValue<'a> for crate::execution::SolidOrImportedGeometry {
1334 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1335 match arg {
1336 KclValue::Solid { value } => Some(Self::Solid(value.clone())),
1337 KclValue::ImportedGeometry(value) => Some(Self::ImportedGeometry(Box::new(value.clone()))),
1338 _ => None,
1339 }
1340 }
1341}
1342
1343impl<'a> FromKclValue<'a> for super::sketch::SketchData {
1344 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1345 let case1 = crate::execution::Plane::from_kcl_val;
1347 let case2 = super::sketch::PlaneData::from_kcl_val;
1348 let case3 = crate::execution::Solid::from_kcl_val;
1349 case1(arg)
1350 .map(Box::new)
1351 .map(Self::Plane)
1352 .or_else(|| case2(arg).map(Self::PlaneOrientation))
1353 .or_else(|| case3(arg).map(Box::new).map(Self::Solid))
1354 }
1355}
1356
1357impl<'a> FromKclValue<'a> for super::axis_or_reference::AxisAndOrigin2d {
1358 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1359 if let Some(s) = arg.as_str() {
1361 return match s {
1362 "X" | "x" => Some(Self::X),
1363 "Y" | "y" => Some(Self::Y),
1364 "-X" | "-x" => Some(Self::NegX),
1365 "-Y" | "-y" => Some(Self::NegY),
1366 _ => None,
1367 };
1368 }
1369 let obj = arg.as_object()?;
1371 let_field_of!(obj, custom, &KclObjectFields);
1372 let_field_of!(custom, origin);
1373 let_field_of!(custom, axis);
1374 Some(Self::Custom { axis, origin })
1375 }
1376}
1377
1378impl<'a> FromKclValue<'a> for super::axis_or_reference::AxisAndOrigin3d {
1379 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1380 if let Some(s) = arg.as_str() {
1382 return match s {
1383 "X" | "x" => Some(Self::X),
1384 "Y" | "y" => Some(Self::Y),
1385 "Z" | "z" => Some(Self::Z),
1386 "-X" | "-x" => Some(Self::NegX),
1387 "-Y" | "-y" => Some(Self::NegY),
1388 "-Z" | "-z" => Some(Self::NegZ),
1389 _ => None,
1390 };
1391 }
1392 let obj = arg.as_object()?;
1394 let_field_of!(obj, custom, &KclObjectFields);
1395 let_field_of!(custom, origin);
1396 let_field_of!(custom, axis);
1397 Some(Self::Custom { axis, origin })
1398 }
1399}
1400
1401impl<'a> FromKclValue<'a> for super::fillet::EdgeReference {
1402 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1403 let id = arg.as_uuid().map(Self::Uuid);
1404 let tag = || TagIdentifier::from_kcl_val(arg).map(Box::new).map(Self::Tag);
1405 id.or_else(tag)
1406 }
1407}
1408
1409impl<'a> FromKclValue<'a> for super::axis_or_reference::Axis2dOrEdgeReference {
1410 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1411 let case1 = super::axis_or_reference::AxisAndOrigin2d::from_kcl_val;
1412 let case2 = super::fillet::EdgeReference::from_kcl_val;
1413 case1(arg).map(Self::Axis).or_else(|| case2(arg).map(Self::Edge))
1414 }
1415}
1416
1417impl<'a> FromKclValue<'a> for super::axis_or_reference::Axis3dOrEdgeReference {
1418 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1419 let case1 = super::axis_or_reference::AxisAndOrigin3d::from_kcl_val;
1420 let case2 = super::fillet::EdgeReference::from_kcl_val;
1421 case1(arg).map(Self::Axis).or_else(|| case2(arg).map(Self::Edge))
1422 }
1423}
1424
1425impl<'a> FromKclValue<'a> for super::mirror::Mirror2dData {
1426 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1427 let obj = arg.as_object()?;
1428 let_field_of!(obj, axis);
1429 Some(Self { axis })
1430 }
1431}
1432
1433impl<'a> FromKclValue<'a> for super::sketch::AngledLineData {
1434 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1435 let case1 = |arg: &KclValue| {
1436 let obj = arg.as_object()?;
1437 let_field_of!(obj, angle);
1438 let_field_of!(obj, length);
1439 Some(Self::AngleAndLengthNamed { angle, length })
1440 };
1441 let case2 = |arg: &KclValue| {
1442 let array = arg.as_array()?;
1443 let ang = array.first()?.as_f64()?;
1444 let len = array.get(1)?.as_f64()?;
1445 Some(Self::AngleAndLengthPair([ang, len]))
1446 };
1447 case1(arg).or_else(|| case2(arg))
1448 }
1449}
1450
1451impl<'a> FromKclValue<'a> for i64 {
1452 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1453 match arg {
1454 KclValue::Number { value, .. } => crate::try_f64_to_i64(*value),
1455 _ => None,
1456 }
1457 }
1458}
1459
1460impl<'a> FromKclValue<'a> for &'a str {
1461 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1462 let KclValue::String { value, meta: _ } = arg else {
1463 return None;
1464 };
1465 Some(value)
1466 }
1467}
1468
1469impl<'a> FromKclValue<'a> for &'a KclObjectFields {
1470 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1471 let KclValue::Object { value, meta: _ } = arg else {
1472 return None;
1473 };
1474 Some(value)
1475 }
1476}
1477
1478impl<'a> FromKclValue<'a> for uuid::Uuid {
1479 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1480 let KclValue::Uuid { value, meta: _ } = arg else {
1481 return None;
1482 };
1483 Some(*value)
1484 }
1485}
1486
1487impl<'a> FromKclValue<'a> for u32 {
1488 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1489 match arg {
1490 KclValue::Number { value, .. } => crate::try_f64_to_u32(*value),
1491 _ => None,
1492 }
1493 }
1494}
1495
1496impl<'a> FromKclValue<'a> for NonZeroU32 {
1497 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1498 u32::from_kcl_val(arg).and_then(|x| x.try_into().ok())
1499 }
1500}
1501
1502impl<'a> FromKclValue<'a> for u64 {
1503 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1504 match arg {
1505 KclValue::Number { value, .. } => crate::try_f64_to_u64(*value),
1506 _ => None,
1507 }
1508 }
1509}
1510impl<'a> FromKclValue<'a> for f64 {
1511 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1512 match arg {
1513 KclValue::Number { value, .. } => Some(*value),
1514 _ => None,
1515 }
1516 }
1517}
1518impl<'a> FromKclValue<'a> for TyF64 {
1519 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1520 match arg {
1521 KclValue::Number { value, ty, .. } => Some(TyF64::new(*value, ty.clone())),
1522 _ => None,
1523 }
1524 }
1525}
1526impl<'a> FromKclValue<'a> for Sketch {
1527 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1528 let KclValue::Sketch { value } = arg else {
1529 return None;
1530 };
1531 Some(value.as_ref().to_owned())
1532 }
1533}
1534
1535impl<'a> FromKclValue<'a> for Helix {
1536 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1537 let KclValue::Helix { value } = arg else {
1538 return None;
1539 };
1540 Some(value.as_ref().to_owned())
1541 }
1542}
1543impl<'a> FromKclValue<'a> for SweepPath {
1544 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1545 let case1 = Sketch::from_kcl_val;
1546 let case2 = Helix::from_kcl_val;
1547 case1(arg)
1548 .map(Self::Sketch)
1549 .or_else(|| case2(arg).map(|arg0: Helix| Self::Helix(Box::new(arg0))))
1550 }
1551}
1552impl<'a> FromKclValue<'a> for String {
1553 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1554 let KclValue::String { value, meta: _ } = arg else {
1555 return None;
1556 };
1557 Some(value.to_owned())
1558 }
1559}
1560impl<'a> FromKclValue<'a> for crate::parsing::ast::types::KclNone {
1561 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1562 let KclValue::KclNone { value, meta: _ } = arg else {
1563 return None;
1564 };
1565 Some(value.to_owned())
1566 }
1567}
1568impl<'a> FromKclValue<'a> for bool {
1569 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1570 let KclValue::Bool { value, meta: _ } = arg else {
1571 return None;
1572 };
1573 Some(*value)
1574 }
1575}
1576
1577impl<'a> FromKclValue<'a> for SketchSet {
1578 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1579 match arg {
1580 KclValue::Sketch { value: sketch } => Some(SketchSet::from(sketch.to_owned())),
1581 KclValue::Sketches { value } => Some(SketchSet::from(value.to_owned())),
1582 KclValue::MixedArray { .. } => {
1583 let v: Option<Vec<Sketch>> = FromKclValue::from_kcl_val(arg);
1584 Some(SketchSet::Sketches(v?.iter().cloned().map(Box::new).collect()))
1585 }
1586 _ => None,
1587 }
1588 }
1589}
1590
1591impl<'a> FromKclValue<'a> for Box<Solid> {
1592 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1593 let KclValue::Solid { value } = arg else {
1594 return None;
1595 };
1596 Some(value.to_owned())
1597 }
1598}
1599
1600impl<'a> FromKclValue<'a> for &'a FunctionSource {
1601 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1602 arg.get_function()
1603 }
1604}
1605
1606impl<'a> FromKclValue<'a> for SolidSet {
1607 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1608 arg.get_solid_set().ok()
1609 }
1610}
1611
1612impl<'a> FromKclValue<'a> for SketchOrSurface {
1613 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1614 match arg {
1615 KclValue::Sketch { value: sg } => Some(Self::Sketch(sg.to_owned())),
1616 KclValue::Plane { value } => Some(Self::SketchSurface(SketchSurface::Plane(value.clone()))),
1617 KclValue::Face { value } => Some(Self::SketchSurface(SketchSurface::Face(value.clone()))),
1618 _ => None,
1619 }
1620 }
1621}
1622impl<'a> FromKclValue<'a> for SketchSurface {
1623 fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
1624 match arg {
1625 KclValue::Plane { value } => Some(Self::Plane(value.clone())),
1626 KclValue::Face { value } => Some(Self::Face(value.clone())),
1627 _ => None,
1628 }
1629 }
1630}
1631
1632impl From<Args> for Metadata {
1633 fn from(value: Args) -> Self {
1634 Self {
1635 source_range: value.source_range,
1636 }
1637 }
1638}
1639
1640impl From<Args> for Vec<Metadata> {
1641 fn from(value: Args) -> Self {
1642 vec![Metadata {
1643 source_range: value.source_range,
1644 }]
1645 }
1646}