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