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