1use std::collections::HashMap;
2
3use anyhow::Result;
4use indexmap::IndexMap;
5use kittycad_modeling_cmds::units::UnitLength;
6use serde::Serialize;
7
8use crate::{
9 CompilationError, KclError, ModuleId, SourceRange,
10 errors::KclErrorDetails,
11 execution::{
12 EnvironmentRef, ExecState, Face, GdtAnnotation, Geometry, GeometryWithImportedGeometry, Helix,
13 ImportedGeometry, Metadata, Plane, Sketch, SketchVar, SketchVarId, Solid, TagIdentifier,
14 annotations::{self, FnAttrs, SETTINGS, SETTINGS_UNIT_LENGTH},
15 types::{NumericType, PrimitiveType, RuntimeType},
16 },
17 parsing::ast::types::{
18 DefaultParamVal, FunctionExpression, KclNone, Literal, LiteralValue, Node, NumericLiteral, TagDeclarator,
19 TagNode, Type,
20 },
21 std::{StdFnProps, args::TyF64},
22};
23
24pub type KclObjectFields = HashMap<String, KclValue>;
25
26#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
28#[ts(export)]
29#[serde(tag = "type")]
30pub enum KclValue {
31 Uuid {
32 value: ::uuid::Uuid,
33 #[serde(skip)]
34 meta: Vec<Metadata>,
35 },
36 Bool {
37 value: bool,
38 #[serde(skip)]
39 meta: Vec<Metadata>,
40 },
41 Number {
42 value: f64,
43 ty: NumericType,
44 #[serde(skip)]
45 meta: Vec<Metadata>,
46 },
47 String {
48 value: String,
49 #[serde(skip)]
50 meta: Vec<Metadata>,
51 },
52 SketchVar {
53 value: Box<SketchVar>,
54 },
55 Tuple {
56 value: Vec<KclValue>,
57 #[serde(skip)]
58 meta: Vec<Metadata>,
59 },
60 HomArray {
62 value: Vec<KclValue>,
63 #[serde(skip)]
65 ty: RuntimeType,
66 },
67 Object {
68 value: KclObjectFields,
69 constrainable: bool,
70 #[serde(skip)]
71 meta: Vec<Metadata>,
72 },
73 TagIdentifier(Box<TagIdentifier>),
74 TagDeclarator(crate::parsing::ast::types::BoxNode<TagDeclarator>),
75 GdtAnnotation {
76 value: Box<GdtAnnotation>,
77 },
78 Plane {
79 value: Box<Plane>,
80 },
81 Face {
82 value: Box<Face>,
83 },
84 Sketch {
85 value: Box<Sketch>,
86 },
87 Solid {
88 value: Box<Solid>,
89 },
90 Helix {
91 value: Box<Helix>,
92 },
93 ImportedGeometry(ImportedGeometry),
94 Function {
95 #[serde(serialize_with = "function_value_stub")]
96 #[ts(type = "null")]
97 value: Box<FunctionSource>,
98 #[serde(skip)]
99 meta: Vec<Metadata>,
100 },
101 Module {
102 value: ModuleId,
103 #[serde(skip)]
104 meta: Vec<Metadata>,
105 },
106 #[ts(skip)]
107 Type {
108 #[serde(skip)]
109 value: TypeDef,
110 experimental: bool,
111 #[serde(skip)]
112 meta: Vec<Metadata>,
113 },
114 KclNone {
115 value: KclNone,
116 #[serde(skip)]
117 meta: Vec<Metadata>,
118 },
119}
120
121fn function_value_stub<S>(_value: &FunctionSource, serializer: S) -> Result<S::Ok, S::Error>
122where
123 S: serde::Serializer,
124{
125 serializer.serialize_unit()
126}
127
128#[derive(Debug, Clone, PartialEq)]
129pub struct FunctionSource {
130 pub input_arg: Option<(String, Option<Type>)>,
131 pub named_args: IndexMap<String, (Option<DefaultParamVal>, Option<Type>)>,
132 pub return_type: Option<Node<Type>>,
133 pub deprecated: bool,
134 pub experimental: bool,
135 pub include_in_feature_tree: bool,
136 pub is_std: bool,
137 pub body: FunctionBody,
138 pub ast: crate::parsing::ast::types::BoxNode<FunctionExpression>,
139}
140
141pub struct KclFunctionSourceParams {
142 pub is_std: bool,
143 pub experimental: bool,
144 pub include_in_feature_tree: bool,
145}
146
147impl FunctionSource {
148 pub fn rust(
149 func: crate::std::StdFn,
150 ast: Box<Node<FunctionExpression>>,
151 _props: StdFnProps,
152 attrs: FnAttrs,
153 ) -> Self {
154 let (input_arg, named_args) = Self::args_from_ast(&ast);
155
156 FunctionSource {
157 input_arg,
158 named_args,
159 return_type: ast.return_type.clone(),
160 deprecated: attrs.deprecated,
161 experimental: attrs.experimental,
162 include_in_feature_tree: attrs.include_in_feature_tree,
163 is_std: true,
164 body: FunctionBody::Rust(func),
165 ast,
166 }
167 }
168
169 pub fn kcl(ast: Box<Node<FunctionExpression>>, memory: EnvironmentRef, params: KclFunctionSourceParams) -> Self {
170 let KclFunctionSourceParams {
171 is_std,
172 experimental,
173 include_in_feature_tree,
174 } = params;
175 let (input_arg, named_args) = Self::args_from_ast(&ast);
176 FunctionSource {
177 input_arg,
178 named_args,
179 return_type: ast.return_type.clone(),
180 deprecated: false,
181 experimental,
182 include_in_feature_tree,
183 is_std,
184 body: FunctionBody::Kcl(memory),
185 ast,
186 }
187 }
188
189 #[allow(clippy::type_complexity)]
190 fn args_from_ast(
191 ast: &FunctionExpression,
192 ) -> (
193 Option<(String, Option<Type>)>,
194 IndexMap<String, (Option<DefaultParamVal>, Option<Type>)>,
195 ) {
196 let mut input_arg = None;
197 let mut named_args = IndexMap::new();
198 for p in &ast.params {
199 if !p.labeled {
200 input_arg = Some((
201 p.identifier.name.clone(),
202 p.param_type.as_ref().map(|t| t.inner.clone()),
203 ));
204 continue;
205 }
206
207 named_args.insert(
208 p.identifier.name.clone(),
209 (p.default_value.clone(), p.param_type.as_ref().map(|t| t.inner.clone())),
210 );
211 }
212
213 (input_arg, named_args)
214 }
215}
216
217#[derive(Debug, Clone, PartialEq)]
218#[allow(unpredictable_function_pointer_comparisons)]
221pub enum FunctionBody {
222 Rust(crate::std::StdFn),
223 Kcl(EnvironmentRef),
224}
225
226#[derive(Debug, Clone, PartialEq)]
227pub enum TypeDef {
228 RustRepr(PrimitiveType, StdFnProps),
229 Alias(RuntimeType),
230}
231
232impl From<Vec<GdtAnnotation>> for KclValue {
233 fn from(mut values: Vec<GdtAnnotation>) -> Self {
234 if values.len() == 1 {
235 let value = values.pop().expect("Just checked len == 1");
236 KclValue::GdtAnnotation { value: Box::new(value) }
237 } else {
238 KclValue::HomArray {
239 value: values
240 .into_iter()
241 .map(|s| KclValue::GdtAnnotation { value: Box::new(s) })
242 .collect(),
243 ty: RuntimeType::Primitive(PrimitiveType::GdtAnnotation),
244 }
245 }
246 }
247}
248
249impl From<Vec<Sketch>> for KclValue {
250 fn from(mut eg: Vec<Sketch>) -> Self {
251 if eg.len() == 1 {
252 KclValue::Sketch {
253 value: Box::new(eg.pop().unwrap()),
254 }
255 } else {
256 KclValue::HomArray {
257 value: eg
258 .into_iter()
259 .map(|s| KclValue::Sketch { value: Box::new(s) })
260 .collect(),
261 ty: RuntimeType::Primitive(PrimitiveType::Sketch),
262 }
263 }
264 }
265}
266
267impl From<Vec<Solid>> for KclValue {
268 fn from(mut eg: Vec<Solid>) -> Self {
269 if eg.len() == 1 {
270 KclValue::Solid {
271 value: Box::new(eg.pop().unwrap()),
272 }
273 } else {
274 KclValue::HomArray {
275 value: eg.into_iter().map(|s| KclValue::Solid { value: Box::new(s) }).collect(),
276 ty: RuntimeType::Primitive(PrimitiveType::Solid),
277 }
278 }
279 }
280}
281
282impl From<KclValue> for Vec<SourceRange> {
283 fn from(item: KclValue) -> Self {
284 match item {
285 KclValue::TagDeclarator(t) => vec![SourceRange::new(t.start, t.end, t.module_id)],
286 KclValue::TagIdentifier(t) => to_vec_sr(&t.meta),
287 KclValue::GdtAnnotation { value } => to_vec_sr(&value.meta),
288 KclValue::Solid { value } => to_vec_sr(&value.meta),
289 KclValue::Sketch { value } => to_vec_sr(&value.meta),
290 KclValue::Helix { value } => to_vec_sr(&value.meta),
291 KclValue::ImportedGeometry(i) => to_vec_sr(&i.meta),
292 KclValue::Function { meta, .. } => to_vec_sr(&meta),
293 KclValue::Plane { value } => to_vec_sr(&value.meta),
294 KclValue::Face { value } => to_vec_sr(&value.meta),
295 KclValue::Bool { meta, .. } => to_vec_sr(&meta),
296 KclValue::Number { meta, .. } => to_vec_sr(&meta),
297 KclValue::String { meta, .. } => to_vec_sr(&meta),
298 KclValue::SketchVar { value, .. } => to_vec_sr(&value.meta),
299 KclValue::Tuple { meta, .. } => to_vec_sr(&meta),
300 KclValue::HomArray { value, .. } => value.iter().flat_map(Into::<Vec<SourceRange>>::into).collect(),
301 KclValue::Object { meta, .. } => to_vec_sr(&meta),
302 KclValue::Module { meta, .. } => to_vec_sr(&meta),
303 KclValue::Uuid { meta, .. } => to_vec_sr(&meta),
304 KclValue::Type { meta, .. } => to_vec_sr(&meta),
305 KclValue::KclNone { meta, .. } => to_vec_sr(&meta),
306 }
307 }
308}
309
310fn to_vec_sr(meta: &[Metadata]) -> Vec<SourceRange> {
311 meta.iter().map(|m| m.source_range).collect()
312}
313
314impl From<&KclValue> for Vec<SourceRange> {
315 fn from(item: &KclValue) -> Self {
316 match item {
317 KclValue::TagDeclarator(t) => vec![SourceRange::new(t.start, t.end, t.module_id)],
318 KclValue::TagIdentifier(t) => to_vec_sr(&t.meta),
319 KclValue::GdtAnnotation { value } => to_vec_sr(&value.meta),
320 KclValue::Solid { value } => to_vec_sr(&value.meta),
321 KclValue::Sketch { value } => to_vec_sr(&value.meta),
322 KclValue::Helix { value } => to_vec_sr(&value.meta),
323 KclValue::ImportedGeometry(i) => to_vec_sr(&i.meta),
324 KclValue::Function { meta, .. } => to_vec_sr(meta),
325 KclValue::Plane { value } => to_vec_sr(&value.meta),
326 KclValue::Face { value } => to_vec_sr(&value.meta),
327 KclValue::Bool { meta, .. } => to_vec_sr(meta),
328 KclValue::Number { meta, .. } => to_vec_sr(meta),
329 KclValue::String { meta, .. } => to_vec_sr(meta),
330 KclValue::SketchVar { value, .. } => to_vec_sr(&value.meta),
331 KclValue::Uuid { meta, .. } => to_vec_sr(meta),
332 KclValue::Tuple { meta, .. } => to_vec_sr(meta),
333 KclValue::HomArray { value, .. } => value.iter().flat_map(Into::<Vec<SourceRange>>::into).collect(),
334 KclValue::Object { meta, .. } => to_vec_sr(meta),
335 KclValue::Module { meta, .. } => to_vec_sr(meta),
336 KclValue::KclNone { meta, .. } => to_vec_sr(meta),
337 KclValue::Type { meta, .. } => to_vec_sr(meta),
338 }
339 }
340}
341
342impl From<&KclValue> for SourceRange {
343 fn from(item: &KclValue) -> Self {
344 let v: Vec<_> = item.into();
345 v.into_iter().next().unwrap_or_default()
346 }
347}
348
349impl KclValue {
350 pub(crate) fn metadata(&self) -> Vec<Metadata> {
351 match self {
352 KclValue::Uuid { value: _, meta } => meta.clone(),
353 KclValue::Bool { value: _, meta } => meta.clone(),
354 KclValue::Number { meta, .. } => meta.clone(),
355 KclValue::String { value: _, meta } => meta.clone(),
356 KclValue::SketchVar { value, .. } => value.meta.clone(),
357 KclValue::Tuple { value: _, meta } => meta.clone(),
358 KclValue::HomArray { value, .. } => value.iter().flat_map(|v| v.metadata()).collect(),
359 KclValue::Object { meta, .. } => meta.clone(),
360 KclValue::TagIdentifier(x) => x.meta.clone(),
361 KclValue::TagDeclarator(x) => vec![x.metadata()],
362 KclValue::GdtAnnotation { value } => value.meta.clone(),
363 KclValue::Plane { value } => value.meta.clone(),
364 KclValue::Face { value } => value.meta.clone(),
365 KclValue::Sketch { value } => value.meta.clone(),
366 KclValue::Solid { value } => value.meta.clone(),
367 KclValue::Helix { value } => value.meta.clone(),
368 KclValue::ImportedGeometry(x) => x.meta.clone(),
369 KclValue::Function { meta, .. } => meta.clone(),
370 KclValue::Module { meta, .. } => meta.clone(),
371 KclValue::KclNone { meta, .. } => meta.clone(),
372 KclValue::Type { meta, .. } => meta.clone(),
373 }
374 }
375
376 #[allow(unused)]
377 pub(crate) fn none() -> Self {
378 Self::KclNone {
379 value: Default::default(),
380 meta: Default::default(),
381 }
382 }
383
384 pub(crate) fn show_variable_in_feature_tree(&self) -> bool {
388 match self {
389 KclValue::Uuid { .. } => false,
390 KclValue::Bool { .. } | KclValue::Number { .. } | KclValue::String { .. } => true,
391 KclValue::SketchVar { .. }
392 | KclValue::Tuple { .. }
393 | KclValue::HomArray { .. }
394 | KclValue::Object { .. }
395 | KclValue::TagIdentifier(_)
396 | KclValue::TagDeclarator(_)
397 | KclValue::GdtAnnotation { .. }
398 | KclValue::Plane { .. }
399 | KclValue::Face { .. }
400 | KclValue::Sketch { .. }
401 | KclValue::Solid { .. }
402 | KclValue::Helix { .. }
403 | KclValue::ImportedGeometry(_)
404 | KclValue::Function { .. }
405 | KclValue::Module { .. }
406 | KclValue::Type { .. }
407 | KclValue::KclNone { .. } => false,
408 }
409 }
410
411 pub(crate) fn human_friendly_type(&self) -> String {
414 match self {
415 KclValue::Uuid { .. } => "a unique ID (uuid)".to_owned(),
416 KclValue::TagDeclarator(_) => "a tag declarator".to_owned(),
417 KclValue::TagIdentifier(_) => "a tag identifier".to_owned(),
418 KclValue::GdtAnnotation { .. } => "an annotation".to_owned(),
419 KclValue::Solid { .. } => "a solid".to_owned(),
420 KclValue::Sketch { .. } => "a sketch".to_owned(),
421 KclValue::Helix { .. } => "a helix".to_owned(),
422 KclValue::ImportedGeometry(_) => "an imported geometry".to_owned(),
423 KclValue::Function { .. } => "a function".to_owned(),
424 KclValue::Plane { .. } => "a plane".to_owned(),
425 KclValue::Face { .. } => "a face".to_owned(),
426 KclValue::Bool { .. } => "a boolean (`true` or `false`)".to_owned(),
427 KclValue::Number {
428 ty: NumericType::Unknown,
429 ..
430 } => "a number with unknown units".to_owned(),
431 KclValue::Number {
432 ty: NumericType::Known(units),
433 ..
434 } => format!("a number ({units})"),
435 KclValue::Number { .. } => "a number".to_owned(),
436 KclValue::String { .. } => "a string".to_owned(),
437 KclValue::SketchVar { .. } => "a sketch variable".to_owned(),
438 KclValue::Object { .. } => "an object".to_owned(),
439 KclValue::Module { .. } => "a module".to_owned(),
440 KclValue::Type { .. } => "a type".to_owned(),
441 KclValue::KclNone { .. } => "none".to_owned(),
442 KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } => {
443 if value.is_empty() {
444 "an empty array".to_owned()
445 } else {
446 const MAX: usize = 3;
448
449 let len = value.len();
450 let element_tys = value
451 .iter()
452 .take(MAX)
453 .map(|elem| elem.principal_type_string())
454 .collect::<Vec<_>>()
455 .join(", ");
456 let mut result = format!("an array of {element_tys}");
457 if len > MAX {
458 result.push_str(&format!(", ... with {len} values"));
459 }
460 if len == 1 {
461 result.push_str(" with 1 value");
462 }
463 result
464 }
465 }
466 }
467 }
468
469 pub(crate) fn from_sketch_var_literal(
470 literal: &Node<NumericLiteral>,
471 id: SketchVarId,
472 exec_state: &ExecState,
473 ) -> Self {
474 let meta = vec![literal.metadata()];
475 let ty = NumericType::from_parsed(literal.suffix, &exec_state.mod_local.settings);
476 KclValue::SketchVar {
477 value: Box::new(SketchVar {
478 id,
479 initial_value: literal.value,
480 meta,
481 ty,
482 }),
483 }
484 }
485
486 pub(crate) fn from_literal(literal: Node<Literal>, exec_state: &mut ExecState) -> Self {
487 let meta = vec![literal.metadata()];
488 match literal.inner.value {
489 LiteralValue::Number { value, suffix } => {
490 let ty = NumericType::from_parsed(suffix, &exec_state.mod_local.settings);
491 if let NumericType::Default { len, .. } = &ty
492 && !exec_state.mod_local.explicit_length_units
493 && *len != UnitLength::Millimeters
494 {
495 exec_state.warn(
496 CompilationError::err(
497 literal.as_source_range(),
498 "Project-wide units are deprecated. Prefer to use per-file default units.",
499 )
500 .with_suggestion(
501 "Fix by adding per-file settings",
502 format!("@{SETTINGS}({SETTINGS_UNIT_LENGTH} = {len})\n"),
503 Some(SourceRange::new(0, 0, literal.module_id)),
505 crate::errors::Tag::Deprecated,
506 ),
507 annotations::WARN_DEPRECATED,
508 );
509 }
510 KclValue::Number { value, meta, ty }
511 }
512 LiteralValue::String(value) => KclValue::String { value, meta },
513 LiteralValue::Bool(value) => KclValue::Bool { value, meta },
514 }
515 }
516
517 pub(crate) fn from_default_param(param: DefaultParamVal, exec_state: &mut ExecState) -> Self {
518 match param {
519 DefaultParamVal::Literal(lit) => Self::from_literal(lit, exec_state),
520 DefaultParamVal::KclNone(value) => KclValue::KclNone {
521 value,
522 meta: Default::default(),
523 },
524 }
525 }
526
527 pub(crate) fn map_env_ref(&self, old_env: usize, new_env: usize) -> Self {
528 let mut result = self.clone();
529 if let KclValue::Function { ref mut value, .. } = result
530 && let FunctionSource {
531 body: FunctionBody::Kcl(memory),
532 ..
533 } = &mut **value
534 {
535 memory.replace_env(old_env, new_env);
536 }
537
538 result
539 }
540
541 pub const fn from_number_with_type(f: f64, ty: NumericType, meta: Vec<Metadata>) -> Self {
542 Self::Number { value: f, meta, ty }
543 }
544
545 pub fn from_point2d(p: [f64; 2], ty: NumericType, meta: Vec<Metadata>) -> Self {
547 let [x, y] = p;
548 Self::Tuple {
549 value: vec![
550 Self::Number {
551 value: x,
552 meta: meta.clone(),
553 ty,
554 },
555 Self::Number {
556 value: y,
557 meta: meta.clone(),
558 ty,
559 },
560 ],
561 meta,
562 }
563 }
564
565 pub fn from_point3d(p: [f64; 3], ty: NumericType, meta: Vec<Metadata>) -> Self {
567 let [x, y, z] = p;
568 Self::Tuple {
569 value: vec![
570 Self::Number {
571 value: x,
572 meta: meta.clone(),
573 ty,
574 },
575 Self::Number {
576 value: y,
577 meta: meta.clone(),
578 ty,
579 },
580 Self::Number {
581 value: z,
582 meta: meta.clone(),
583 ty,
584 },
585 ],
586 meta,
587 }
588 }
589
590 pub fn array_from_point3d(p: [f64; 3], ty: NumericType, meta: Vec<Metadata>) -> Self {
592 let [x, y, z] = p;
593 Self::HomArray {
594 value: vec![
595 Self::Number {
596 value: x,
597 meta: meta.clone(),
598 ty,
599 },
600 Self::Number {
601 value: y,
602 meta: meta.clone(),
603 ty,
604 },
605 Self::Number { value: z, meta, ty },
606 ],
607 ty: ty.into(),
608 }
609 }
610
611 pub(crate) fn as_usize(&self) -> Option<usize> {
612 match self {
613 KclValue::Number { value, .. } => crate::try_f64_to_usize(*value),
614 _ => None,
615 }
616 }
617
618 pub fn as_int(&self) -> Option<i64> {
619 match self {
620 KclValue::Number { value, .. } => crate::try_f64_to_i64(*value),
621 _ => None,
622 }
623 }
624
625 pub fn as_int_with_ty(&self) -> Option<(i64, NumericType)> {
626 match self {
627 KclValue::Number { value, ty, .. } => crate::try_f64_to_i64(*value).map(|i| (i, *ty)),
628 _ => None,
629 }
630 }
631
632 pub fn as_object(&self) -> Option<&KclObjectFields> {
633 match self {
634 KclValue::Object { value, .. } => Some(value),
635 _ => None,
636 }
637 }
638
639 pub fn into_object(self) -> Option<KclObjectFields> {
640 match self {
641 KclValue::Object { value, .. } => Some(value),
642 _ => None,
643 }
644 }
645
646 pub fn as_str(&self) -> Option<&str> {
647 match self {
648 KclValue::String { value, .. } => Some(value),
649 _ => None,
650 }
651 }
652
653 pub fn into_array(self) -> Vec<KclValue> {
654 match self {
655 KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } => value,
656 _ => vec![self],
657 }
658 }
659
660 pub fn as_point2d(&self) -> Option<[TyF64; 2]> {
661 let value = match self {
662 KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } => value,
663 _ => return None,
664 };
665
666 if value.len() != 2 {
667 return None;
668 }
669 let x = value[0].as_ty_f64()?;
670 let y = value[1].as_ty_f64()?;
671 Some([x, y])
672 }
673
674 pub fn as_point3d(&self) -> Option<[TyF64; 3]> {
675 let value = match self {
676 KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } => value,
677 _ => return None,
678 };
679
680 if value.len() != 3 {
681 return None;
682 }
683 let x = value[0].as_ty_f64()?;
684 let y = value[1].as_ty_f64()?;
685 let z = value[2].as_ty_f64()?;
686 Some([x, y, z])
687 }
688
689 pub fn as_uuid(&self) -> Option<uuid::Uuid> {
690 match self {
691 KclValue::Uuid { value, .. } => Some(*value),
692 _ => None,
693 }
694 }
695
696 pub fn as_plane(&self) -> Option<&Plane> {
697 match self {
698 KclValue::Plane { value, .. } => Some(value),
699 _ => None,
700 }
701 }
702
703 pub fn as_solid(&self) -> Option<&Solid> {
704 match self {
705 KclValue::Solid { value, .. } => Some(value),
706 _ => None,
707 }
708 }
709
710 pub fn as_sketch(&self) -> Option<&Sketch> {
711 match self {
712 KclValue::Sketch { value, .. } => Some(value),
713 _ => None,
714 }
715 }
716
717 pub fn as_mut_sketch(&mut self) -> Option<&mut Sketch> {
718 match self {
719 KclValue::Sketch { value } => Some(value),
720 _ => None,
721 }
722 }
723
724 pub fn as_sketch_var(&self) -> Option<&SketchVar> {
725 match self {
726 KclValue::SketchVar { value, .. } => Some(value),
727 _ => None,
728 }
729 }
730
731 pub fn as_mut_tag(&mut self) -> Option<&mut TagIdentifier> {
732 match self {
733 KclValue::TagIdentifier(value) => Some(value),
734 _ => None,
735 }
736 }
737
738 #[cfg(test)]
739 pub fn as_f64(&self) -> Option<f64> {
740 match self {
741 KclValue::Number { value, .. } => Some(*value),
742 _ => None,
743 }
744 }
745
746 pub fn as_ty_f64(&self) -> Option<TyF64> {
747 match self {
748 KclValue::Number { value, ty, .. } => Some(TyF64::new(*value, *ty)),
749 _ => None,
750 }
751 }
752
753 pub fn as_bool(&self) -> Option<bool> {
754 match self {
755 KclValue::Bool { value, .. } => Some(*value),
756 _ => None,
757 }
758 }
759
760 pub fn as_function(&self) -> Option<&FunctionSource> {
762 match self {
763 KclValue::Function { value, .. } => Some(value),
764 _ => None,
765 }
766 }
767
768 pub fn get_tag_identifier(&self) -> Result<TagIdentifier, KclError> {
770 match self {
771 KclValue::TagIdentifier(t) => Ok(*t.clone()),
772 _ => Err(KclError::new_semantic(KclErrorDetails::new(
773 format!("Not a tag identifier: {self:?}"),
774 self.clone().into(),
775 ))),
776 }
777 }
778
779 pub fn get_tag_declarator(&self) -> Result<TagNode, KclError> {
781 match self {
782 KclValue::TagDeclarator(t) => Ok((**t).clone()),
783 _ => Err(KclError::new_semantic(KclErrorDetails::new(
784 format!("Not a tag declarator: {self:?}"),
785 self.clone().into(),
786 ))),
787 }
788 }
789
790 pub fn get_bool(&self) -> Result<bool, KclError> {
792 self.as_bool().ok_or_else(|| {
793 KclError::new_type(KclErrorDetails::new(
794 format!("Expected bool, found {}", self.human_friendly_type()),
795 self.into(),
796 ))
797 })
798 }
799
800 pub fn is_unknown_number(&self) -> bool {
801 match self {
802 KclValue::Number { ty, .. } => !ty.is_fully_specified(),
803 _ => false,
804 }
805 }
806
807 pub fn value_str(&self) -> Option<String> {
808 match self {
809 KclValue::Bool { value, .. } => Some(format!("{value}")),
810 KclValue::Number { value, .. } => Some(format!("{value}")),
812 KclValue::String { value, .. } => Some(format!("'{value}'")),
813 KclValue::SketchVar { value, .. } => Some(format!("var {}", value.initial_value)),
815 KclValue::Uuid { value, .. } => Some(format!("{value}")),
816 KclValue::TagDeclarator(tag) => Some(format!("${}", tag.name)),
817 KclValue::TagIdentifier(tag) => Some(format!("${}", tag.value)),
818 KclValue::Tuple { .. } => Some("[...]".to_owned()),
820 KclValue::HomArray { .. } => Some("[...]".to_owned()),
821 KclValue::Object { .. } => Some("{ ... }".to_owned()),
822 KclValue::Module { .. }
823 | KclValue::GdtAnnotation { .. }
824 | KclValue::Solid { .. }
825 | KclValue::Sketch { .. }
826 | KclValue::Helix { .. }
827 | KclValue::ImportedGeometry(_)
828 | KclValue::Function { .. }
829 | KclValue::Plane { .. }
830 | KclValue::Face { .. }
831 | KclValue::KclNone { .. }
832 | KclValue::Type { .. } => None,
833 }
834 }
835}
836
837impl From<Geometry> for KclValue {
838 fn from(value: Geometry) -> Self {
839 match value {
840 Geometry::Sketch(x) => Self::Sketch { value: Box::new(x) },
841 Geometry::Solid(x) => Self::Solid { value: Box::new(x) },
842 }
843 }
844}
845
846impl From<GeometryWithImportedGeometry> for KclValue {
847 fn from(value: GeometryWithImportedGeometry) -> Self {
848 match value {
849 GeometryWithImportedGeometry::Sketch(x) => Self::Sketch { value: Box::new(x) },
850 GeometryWithImportedGeometry::Solid(x) => Self::Solid { value: Box::new(x) },
851 GeometryWithImportedGeometry::ImportedGeometry(x) => Self::ImportedGeometry(*x),
852 }
853 }
854}
855
856#[cfg(test)]
857mod tests {
858 use super::*;
859 use crate::exec::UnitType;
860
861 #[test]
862 fn test_human_friendly_type() {
863 let len = KclValue::Number {
864 value: 1.0,
865 ty: NumericType::Known(UnitType::GenericLength),
866 meta: vec![],
867 };
868 assert_eq!(len.human_friendly_type(), "a number (Length)".to_string());
869
870 let unknown = KclValue::Number {
871 value: 1.0,
872 ty: NumericType::Unknown,
873 meta: vec![],
874 };
875 assert_eq!(unknown.human_friendly_type(), "a number with unknown units".to_string());
876
877 let mm = KclValue::Number {
878 value: 1.0,
879 ty: NumericType::Known(UnitType::Length(UnitLength::Millimeters)),
880 meta: vec![],
881 };
882 assert_eq!(mm.human_friendly_type(), "a number (mm)".to_string());
883
884 let array1_mm = KclValue::HomArray {
885 value: vec![mm.clone()],
886 ty: RuntimeType::any(),
887 };
888 assert_eq!(
889 array1_mm.human_friendly_type(),
890 "an array of `number(mm)` with 1 value".to_string()
891 );
892
893 let array2_mm = KclValue::HomArray {
894 value: vec![mm.clone(), mm.clone()],
895 ty: RuntimeType::any(),
896 };
897 assert_eq!(
898 array2_mm.human_friendly_type(),
899 "an array of `number(mm)`, `number(mm)`".to_string()
900 );
901
902 let array3_mm = KclValue::HomArray {
903 value: vec![mm.clone(), mm.clone(), mm.clone()],
904 ty: RuntimeType::any(),
905 };
906 assert_eq!(
907 array3_mm.human_friendly_type(),
908 "an array of `number(mm)`, `number(mm)`, `number(mm)`".to_string()
909 );
910
911 let inches = KclValue::Number {
912 value: 1.0,
913 ty: NumericType::Known(UnitType::Length(UnitLength::Inches)),
914 meta: vec![],
915 };
916 let array4 = KclValue::HomArray {
917 value: vec![mm.clone(), mm.clone(), inches, mm],
918 ty: RuntimeType::any(),
919 };
920 assert_eq!(
921 array4.human_friendly_type(),
922 "an array of `number(mm)`, `number(mm)`, `number(in)`, ... with 4 values".to_string()
923 );
924
925 let empty_array = KclValue::HomArray {
926 value: vec![],
927 ty: RuntimeType::any(),
928 };
929 assert_eq!(empty_array.human_friendly_type(), "an empty array".to_string());
930
931 let array_nested = KclValue::HomArray {
932 value: vec![array2_mm],
933 ty: RuntimeType::any(),
934 };
935 assert_eq!(
936 array_nested.human_friendly_type(),
937 "an array of `[any; 2]` with 1 value".to_string()
938 );
939 }
940}