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