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