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