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