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