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