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