1use std::collections::HashMap;
2use std::sync::Arc;
3
4use anyhow::Result;
5use indexmap::IndexMap;
6use kcl_api::UnitLength;
7use serde::Serialize;
8use serde::Serializer;
9
10use crate::CompilationIssue;
11use crate::KclError;
12use crate::ModuleId;
13use crate::SourceRange;
14use crate::errors::KclErrorDetails;
15use crate::execution::AbstractSegment;
16use crate::execution::BoundedEdge;
17use crate::execution::EnvironmentRef;
18use crate::execution::ExecState;
19use crate::execution::Face;
20use crate::execution::GdtAnnotation;
21use crate::execution::Geometry;
22use crate::execution::GeometryWithImportedGeometry;
23use crate::execution::Helix;
24use crate::execution::ImportedGeometry;
25use crate::execution::Metadata;
26use crate::execution::Plane;
27use crate::execution::Segment;
28use crate::execution::SegmentRepr;
29use crate::execution::Sketch;
30use crate::execution::SketchConstraint;
31use crate::execution::SketchVar;
32use crate::execution::SketchVarId;
33use crate::execution::Solid;
34use crate::execution::TagIdentifier;
35use crate::execution::UnsolvedExpr;
36use crate::execution::annotations::FnAttrs;
37use crate::execution::annotations::SETTINGS;
38use crate::execution::annotations::SETTINGS_UNIT_LENGTH;
39use crate::execution::annotations::VersionConstraint;
40use crate::execution::annotations::{self};
41use crate::execution::types::NumericType;
42use crate::execution::types::NumericTypeExt;
43use crate::execution::types::PrimitiveType;
44use crate::execution::types::RuntimeType;
45use crate::parsing::ast::types::DefaultParamVal;
46use crate::parsing::ast::types::FunctionExpression;
47use crate::parsing::ast::types::KclNone;
48use crate::parsing::ast::types::Literal;
49use crate::parsing::ast::types::LiteralValue;
50use crate::parsing::ast::types::Node;
51use crate::parsing::ast::types::NumericLiteral;
52use crate::parsing::ast::types::TagDeclarator;
53use crate::parsing::ast::types::TagNode;
54use crate::parsing::ast::types::Type;
55use crate::std::StdFnProps;
56use crate::std::args::TyF64;
57
58pub type KclObjectFields = HashMap<String, KclValue>;
59
60#[derive(Debug, Clone, Default, PartialEq, Serialize)]
61pub enum KclObjectKind {
62 #[default]
63 Default,
64 SketchTags {
65 #[serde(default, skip_serializing_if = "Vec::is_empty")]
66 deprecated_solid_tag_names: Vec<String>,
67 },
68}
69
70impl KclObjectKind {
71 pub(crate) fn is_default(&self) -> bool {
72 match self {
73 KclObjectKind::Default => true,
74 KclObjectKind::SketchTags { .. } => false,
75 }
76 }
77
78 pub(crate) fn deprecated_solid_tag_names(&self) -> &[String] {
79 match self {
80 Self::Default => &[],
81 Self::SketchTags {
82 deprecated_solid_tag_names,
83 } => deprecated_solid_tag_names,
84 }
85 }
86}
87
88#[derive(Debug, Clone, Serialize, PartialEq)]
90#[serde(tag = "type")]
91pub enum KclValue {
92 Uuid {
93 value: ::uuid::Uuid,
94 #[serde(skip)]
95 meta: Vec<Metadata>,
96 },
97 Bool {
98 value: bool,
99 #[serde(skip)]
100 meta: Vec<Metadata>,
101 },
102 Number {
103 value: f64,
104 ty: NumericType,
105 #[serde(skip)]
106 meta: Vec<Metadata>,
107 },
108 String {
109 value: String,
110 #[serde(skip)]
111 meta: Vec<Metadata>,
112 },
113 Enum {
114 value: Box<EnumValue>,
115 },
116 SketchVar {
117 value: Box<SketchVar>,
118 },
119 SketchConstraint {
120 value: Box<SketchConstraint>,
121 },
122 Tuple {
123 value: Vec<KclValue>,
124 #[serde(skip)]
125 meta: Vec<Metadata>,
126 },
127 HomArray {
129 value: Vec<KclValue>,
130 #[serde(skip)]
132 ty: RuntimeType,
133 },
134 Object {
135 value: KclObjectFields,
136 constrainable: bool,
137 #[serde(default, skip_serializing_if = "KclObjectKind::is_default")]
138 object_kind: KclObjectKind,
139 #[serde(skip)]
140 meta: Vec<Metadata>,
141 },
142 TagIdentifier(Box<TagIdentifier>),
143 TagDeclarator(crate::parsing::ast::types::BoxNode<TagDeclarator>),
144 GdtAnnotation {
145 value: Box<GdtAnnotation>,
146 },
147 Plane {
148 value: Box<Plane>,
149 },
150 Face {
151 value: Box<Face>,
152 },
153 BoundedEdge {
154 value: BoundedEdge,
155 meta: Vec<Metadata>,
156 },
157 Segment {
158 value: Box<AbstractSegment>,
159 },
160 Sketch {
161 value: Box<Sketch>,
162 },
163 Solid {
164 value: Box<Solid>,
165 },
166 Helix {
167 value: Box<Helix>,
168 },
169 ImportedGeometry(ImportedGeometry),
170 Function {
171 #[serde(serialize_with = "function_value_stub")]
172 value: Box<FunctionSource>,
173 #[serde(skip)]
174 meta: Vec<Metadata>,
175 },
176 Module {
177 value: ModuleId,
178 #[serde(skip)]
179 meta: Vec<Metadata>,
180 },
181 Type {
182 #[serde(skip)]
183 value: TypeDef,
184 experimental: bool,
185 #[serde(skip)]
186 meta: Vec<Metadata>,
187 },
188 KclNone {
189 value: KclNone,
190 #[serde(skip)]
191 meta: Vec<Metadata>,
192 },
193}
194
195fn function_value_stub<S>(_value: &FunctionSource, serializer: S) -> Result<S::Ok, S::Error>
196where
197 S: serde::Serializer,
198{
199 serializer.serialize_unit()
200}
201
202#[derive(Debug, Clone, PartialEq)]
203pub struct NamedParam {
204 pub experimental: bool,
205 pub deprecated: bool,
207 pub deprecated_since: Option<VersionConstraint>,
209 pub default_value: Option<DefaultParamVal>,
210 pub ty: Option<Type>,
211}
212
213#[derive(Debug, Clone, PartialEq)]
214pub struct FunctionSource {
215 pub input_arg: Option<(String, Option<Type>)>,
216 pub named_args: IndexMap<String, NamedParam>,
217 pub return_type: Option<Node<Type>>,
218 pub deprecated: bool,
219 pub deprecated_since: Option<VersionConstraint>,
223 pub experimental: bool,
224 pub include_in_feature_tree: bool,
225 pub std_props: Option<StdFnProps>,
226 pub body: FunctionBody,
227 pub ast: crate::parsing::ast::types::BoxNode<FunctionExpression>,
228}
229
230pub struct KclFunctionSourceParams {
231 pub std_props: Option<StdFnProps>,
232 pub experimental: bool,
233 pub include_in_feature_tree: bool,
234}
235
236impl FunctionSource {
237 pub fn rust(
238 func: crate::std::StdFn,
239 ast: Box<Node<FunctionExpression>>,
240 props: StdFnProps,
241 attrs: FnAttrs,
242 ) -> Self {
243 let (input_arg, named_args) = Self::args_from_ast(&ast);
244
245 FunctionSource {
246 input_arg,
247 named_args,
248 return_type: ast.return_type.clone(),
249 deprecated: attrs.deprecated,
250 deprecated_since: attrs.deprecated_since,
251 experimental: attrs.experimental,
252 include_in_feature_tree: attrs.include_in_feature_tree,
253 std_props: Some(props),
254 body: FunctionBody::Rust(func),
255 ast,
256 }
257 }
258
259 pub fn kcl(ast: Box<Node<FunctionExpression>>, memory: EnvironmentRef, params: KclFunctionSourceParams) -> Self {
260 let KclFunctionSourceParams {
261 std_props,
262 experimental,
263 include_in_feature_tree,
264 } = params;
265 let (input_arg, named_args) = Self::args_from_ast(&ast);
266 FunctionSource {
267 input_arg,
268 named_args,
269 return_type: ast.return_type.clone(),
270 deprecated: false,
271 deprecated_since: None,
272 experimental,
273 include_in_feature_tree,
274 std_props,
275 body: FunctionBody::Kcl(memory),
276 ast,
277 }
278 }
279
280 #[expect(clippy::type_complexity)]
281 fn args_from_ast(ast: &FunctionExpression) -> (Option<(String, Option<Type>)>, IndexMap<String, NamedParam>) {
282 let mut input_arg = None;
283 let mut named_args = IndexMap::new();
284 for p in &ast.params {
285 if !p.labeled {
286 input_arg = Some((
287 p.identifier.name.clone(),
288 p.param_type.as_ref().map(|t| t.inner.clone()),
289 ));
290 continue;
291 }
292
293 named_args.insert(
294 p.identifier.name.clone(),
295 NamedParam {
296 experimental: p.experimental,
297 deprecated: p.deprecated,
298 deprecated_since: p.deprecated_since.clone(),
299 default_value: p.default_value.clone(),
300 ty: p.param_type.as_ref().map(|t| t.inner.clone()),
301 },
302 );
303 }
304
305 (input_arg, named_args)
306 }
307
308 pub(crate) fn is_std(&self) -> bool {
309 self.std_props.is_some()
310 }
311}
312
313#[derive(Debug, Clone, PartialEq)]
314#[allow(unpredictable_function_pointer_comparisons)]
317pub enum FunctionBody {
318 Rust(crate::std::StdFn),
319 Kcl(EnvironmentRef),
320}
321
322#[derive(Debug, Clone, PartialEq)]
323pub enum TypeDef {
324 RustRepr(PrimitiveType, StdFnProps),
325 Alias(RuntimeType),
326 Enum(Arc<EnumTypeDef>),
330}
331
332#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
340pub struct EnumTypeId {
341 module_id: ModuleId,
342 declared_name: String,
343}
344
345impl EnumTypeId {
346 pub fn new(module_id: ModuleId, declared_name: impl Into<String>) -> Self {
347 Self {
348 module_id,
349 declared_name: declared_name.into(),
350 }
351 }
352
353 pub fn module_id(&self) -> ModuleId {
354 self.module_id
355 }
356
357 pub fn declared_name(&self) -> &str {
360 &self.declared_name
361 }
362}
363
364#[derive(Debug, Clone, PartialEq)]
366pub struct EnumTypeDef {
367 id: EnumTypeId,
368 variants: Vec<String>,
369}
370
371#[derive(Debug, Clone, PartialEq)]
379pub struct DuplicateVariant {
380 pub name: String,
382 pub first_index: usize,
384 pub duplicate_index: usize,
386}
387
388impl EnumTypeDef {
389 pub fn new(id: EnumTypeId, variants: Vec<String>) -> Result<Self, DuplicateVariant> {
396 for (duplicate_index, variant) in variants.iter().enumerate() {
397 if let Some(first_index) = variants[..duplicate_index].iter().position(|v| v == variant) {
398 return Err(DuplicateVariant {
399 name: variant.clone(),
400 first_index,
401 duplicate_index,
402 });
403 }
404 }
405
406 Ok(Self { id, variants })
407 }
408
409 pub fn id(&self) -> &EnumTypeId {
410 &self.id
411 }
412
413 pub fn variants(&self) -> &[String] {
414 &self.variants
415 }
416
417 pub fn has_variant(&self, name: &str) -> bool {
418 self.variants.iter().any(|v| v == name)
419 }
420}
421
422#[derive(Debug, Clone, Serialize)]
433pub struct EnumValue {
434 #[serde(rename = "enum_id", serialize_with = "serialize_enum_def_id")]
438 def: Arc<EnumTypeDef>,
439 variant: String,
440 #[serde(skip)]
441 meta: Vec<Metadata>,
442}
443
444fn serialize_enum_def_id<S: Serializer>(def: &Arc<EnumTypeDef>, serializer: S) -> Result<S::Ok, S::Error> {
445 def.id().serialize(serializer)
446}
447
448impl PartialEq for EnumValue {
453 fn eq(&self, other: &Self) -> bool {
454 self.def.id() == other.def.id() && self.variant == other.variant
455 }
456}
457
458impl EnumValue {
459 pub fn new(def: Arc<EnumTypeDef>, variant: impl Into<String>, meta: Vec<Metadata>) -> Self {
460 Self {
461 def,
462 variant: variant.into(),
463 meta,
464 }
465 }
466
467 pub fn enum_id(&self) -> &EnumTypeId {
468 self.def.id()
469 }
470
471 pub fn variant(&self) -> &str {
472 &self.variant
473 }
474
475 pub fn meta(&self) -> &[Metadata] {
476 &self.meta
477 }
478
479 pub fn declared_string_repr(&self) -> String {
487 self.variant.clone()
488 }
489
490 pub fn qualified_name(&self) -> String {
492 format!("{}::{}", self.def.id().declared_name(), self.variant)
493 }
494}
495
496impl From<Vec<GdtAnnotation>> for KclValue {
497 fn from(mut values: Vec<GdtAnnotation>) -> Self {
498 if values.len() == 1 {
499 let value = values.pop().expect("Just checked len == 1");
500 KclValue::GdtAnnotation { value: Box::new(value) }
501 } else {
502 KclValue::HomArray {
503 value: values
504 .into_iter()
505 .map(|s| KclValue::GdtAnnotation { value: Box::new(s) })
506 .collect(),
507 ty: RuntimeType::Primitive(PrimitiveType::GdtAnnotation),
508 }
509 }
510 }
511}
512
513impl From<Vec<Sketch>> for KclValue {
514 fn from(mut eg: Vec<Sketch>) -> Self {
515 if eg.len() == 1
516 && let Some(s) = eg.pop()
517 {
518 KclValue::Sketch { value: Box::new(s) }
519 } else {
520 KclValue::HomArray {
521 value: eg
522 .into_iter()
523 .map(|s| KclValue::Sketch { value: Box::new(s) })
524 .collect(),
525 ty: RuntimeType::Primitive(PrimitiveType::Sketch),
526 }
527 }
528 }
529}
530
531impl From<Vec<Solid>> for KclValue {
532 fn from(mut eg: Vec<Solid>) -> Self {
533 if eg.len() == 1
534 && let Some(s) = eg.pop()
535 {
536 KclValue::Solid { value: Box::new(s) }
537 } else {
538 KclValue::HomArray {
539 value: eg.into_iter().map(|s| KclValue::Solid { value: Box::new(s) }).collect(),
540 ty: RuntimeType::Primitive(PrimitiveType::Solid),
541 }
542 }
543 }
544}
545
546impl From<KclValue> for Vec<SourceRange> {
547 fn from(item: KclValue) -> Self {
548 match item {
549 KclValue::TagDeclarator(t) => vec![SourceRange::new(t.start, t.end, t.module_id)],
550 KclValue::TagIdentifier(t) => to_vec_sr(&t.meta),
551 KclValue::GdtAnnotation { value } => to_vec_sr(&value.meta),
552 KclValue::Solid { value } => to_vec_sr(&value.meta),
553 KclValue::Sketch { value } => to_vec_sr(&value.meta),
554 KclValue::Helix { value } => to_vec_sr(&value.meta),
555 KclValue::ImportedGeometry(i) => to_vec_sr(&i.meta),
556 KclValue::Function { meta, .. } => to_vec_sr(&meta),
557 KclValue::Plane { value } => to_vec_sr(&value.meta),
558 KclValue::Face { value } => to_vec_sr(&value.meta),
559 KclValue::Segment { value } => to_vec_sr(&value.meta),
560 KclValue::Bool { meta, .. } => to_vec_sr(&meta),
561 KclValue::Number { meta, .. } => to_vec_sr(&meta),
562 KclValue::String { meta, .. } => to_vec_sr(&meta),
563 KclValue::Enum { value } => to_vec_sr(value.meta()),
564 KclValue::SketchVar { value, .. } => to_vec_sr(&value.meta),
565 KclValue::SketchConstraint { value, .. } => to_vec_sr(&value.meta),
566 KclValue::Tuple { meta, .. } => to_vec_sr(&meta),
567 KclValue::HomArray { value, .. } => value.iter().flat_map(Into::<Vec<SourceRange>>::into).collect(),
568 KclValue::Object { meta, .. } => to_vec_sr(&meta),
569 KclValue::Module { meta, .. } => to_vec_sr(&meta),
570 KclValue::Uuid { meta, .. } => to_vec_sr(&meta),
571 KclValue::Type { meta, .. } => to_vec_sr(&meta),
572 KclValue::KclNone { meta, .. } => to_vec_sr(&meta),
573 KclValue::BoundedEdge { meta, .. } => to_vec_sr(&meta),
574 }
575 }
576}
577
578fn to_vec_sr(meta: &[Metadata]) -> Vec<SourceRange> {
579 meta.iter().map(|m| m.source_range).collect()
580}
581
582impl From<&KclValue> for Vec<SourceRange> {
583 fn from(item: &KclValue) -> Self {
584 match item {
585 KclValue::TagDeclarator(t) => vec![SourceRange::new(t.start, t.end, t.module_id)],
586 KclValue::TagIdentifier(t) => to_vec_sr(&t.meta),
587 KclValue::GdtAnnotation { value } => to_vec_sr(&value.meta),
588 KclValue::Solid { value } => to_vec_sr(&value.meta),
589 KclValue::Sketch { value } => to_vec_sr(&value.meta),
590 KclValue::Helix { value } => to_vec_sr(&value.meta),
591 KclValue::ImportedGeometry(i) => to_vec_sr(&i.meta),
592 KclValue::Function { meta, .. } => to_vec_sr(meta),
593 KclValue::Plane { value } => to_vec_sr(&value.meta),
594 KclValue::Face { value } => to_vec_sr(&value.meta),
595 KclValue::Segment { value } => to_vec_sr(&value.meta),
596 KclValue::Bool { meta, .. } => to_vec_sr(meta),
597 KclValue::Number { meta, .. } => to_vec_sr(meta),
598 KclValue::String { meta, .. } => to_vec_sr(meta),
599 KclValue::Enum { value } => to_vec_sr(value.meta()),
600 KclValue::SketchVar { value, .. } => to_vec_sr(&value.meta),
601 KclValue::SketchConstraint { value, .. } => to_vec_sr(&value.meta),
602 KclValue::Uuid { meta, .. } => to_vec_sr(meta),
603 KclValue::Tuple { meta, .. } => to_vec_sr(meta),
604 KclValue::HomArray { value, .. } => value.iter().flat_map(Into::<Vec<SourceRange>>::into).collect(),
605 KclValue::Object { meta, .. } => to_vec_sr(meta),
606 KclValue::Module { meta, .. } => to_vec_sr(meta),
607 KclValue::KclNone { meta, .. } => to_vec_sr(meta),
608 KclValue::Type { meta, .. } => to_vec_sr(meta),
609 KclValue::BoundedEdge { meta, .. } => to_vec_sr(meta),
610 }
611 }
612}
613
614impl From<&KclValue> for SourceRange {
615 fn from(item: &KclValue) -> Self {
616 let v: Vec<_> = item.into();
617 v.into_iter().next().unwrap_or_default()
618 }
619}
620
621impl KclValue {
622 pub(crate) fn metadata(&self) -> Vec<Metadata> {
623 match self {
624 KclValue::Uuid { value: _, meta } => meta.clone(),
625 KclValue::Bool { value: _, meta } => meta.clone(),
626 KclValue::Number { meta, .. } => meta.clone(),
627 KclValue::String { value: _, meta } => meta.clone(),
628 KclValue::Enum { value } => value.meta().to_vec(),
629 KclValue::SketchVar { value, .. } => value.meta.clone(),
630 KclValue::SketchConstraint { value, .. } => value.meta.clone(),
631 KclValue::Tuple { value: _, meta } => meta.clone(),
632 KclValue::HomArray { value, .. } => value.iter().flat_map(|v| v.metadata()).collect(),
633 KclValue::Object { meta, .. } => meta.clone(),
634 KclValue::TagIdentifier(x) => x.meta.clone(),
635 KclValue::TagDeclarator(x) => vec![x.metadata()],
636 KclValue::GdtAnnotation { value } => value.meta.clone(),
637 KclValue::Plane { value } => value.meta.clone(),
638 KclValue::Face { value } => value.meta.clone(),
639 KclValue::Segment { value } => value.meta.clone(),
640 KclValue::Sketch { value } => value.meta.clone(),
641 KclValue::Solid { value } => value.meta.clone(),
642 KclValue::Helix { value } => value.meta.clone(),
643 KclValue::ImportedGeometry(x) => x.meta.clone(),
644 KclValue::Function { meta, .. } => meta.clone(),
645 KclValue::Module { meta, .. } => meta.clone(),
646 KclValue::KclNone { meta, .. } => meta.clone(),
647 KclValue::Type { meta, .. } => meta.clone(),
648 KclValue::BoundedEdge { meta, .. } => meta.clone(),
649 }
650 }
651
652 #[allow(unused)]
653 pub(crate) fn none() -> Self {
654 Self::KclNone {
655 value: Default::default(),
656 meta: Default::default(),
657 }
658 }
659
660 pub(crate) fn show_variable_in_feature_tree(&self) -> bool {
664 match self {
665 KclValue::Uuid { .. } => false,
666 KclValue::Bool { .. } | KclValue::Number { .. } | KclValue::String { .. } | KclValue::Enum { .. } => true,
667 KclValue::SketchVar { .. }
668 | KclValue::SketchConstraint { .. }
669 | KclValue::Tuple { .. }
670 | KclValue::HomArray { .. }
671 | KclValue::Object { .. }
672 | KclValue::TagIdentifier(_)
673 | KclValue::TagDeclarator(_)
674 | KclValue::GdtAnnotation { .. }
675 | KclValue::Plane { .. }
676 | KclValue::Face { .. }
677 | KclValue::Segment { .. }
678 | KclValue::Sketch { .. }
679 | KclValue::Solid { .. }
680 | KclValue::Helix { .. }
681 | KclValue::ImportedGeometry(_)
682 | KclValue::Function { .. }
683 | KclValue::Module { .. }
684 | KclValue::Type { .. }
685 | KclValue::BoundedEdge { .. }
686 | KclValue::KclNone { .. } => false,
687 }
688 }
689
690 pub(crate) fn human_friendly_type(&self) -> String {
693 match self {
694 KclValue::Uuid { .. } => "a unique ID (uuid)".to_owned(),
695 KclValue::TagDeclarator(_) => "a tag declarator".to_owned(),
696 KclValue::TagIdentifier(_) => "a tag identifier".to_owned(),
697 KclValue::GdtAnnotation { .. } => "an annotation".to_owned(),
698 KclValue::Solid { .. } => "a solid".to_owned(),
699 KclValue::Sketch { .. } => "a sketch".to_owned(),
700 KclValue::Helix { .. } => "a helix".to_owned(),
701 KclValue::ImportedGeometry(_) => "an imported geometry".to_owned(),
702 KclValue::Function { .. } => "a function".to_owned(),
703 KclValue::Plane { .. } => "a plane".to_owned(),
704 KclValue::Face { .. } => "a face".to_owned(),
705 KclValue::Segment { .. } => "a segment".to_owned(),
706 KclValue::Bool { .. } => "a boolean (`true` or `false`)".to_owned(),
707 KclValue::Number {
708 ty: NumericType::Unknown,
709 ..
710 } => "a number with unknown units".to_owned(),
711 KclValue::Number {
712 ty: NumericType::Known(units),
713 ..
714 } => format!("a number ({units})"),
715 KclValue::Number { .. } => "a number".to_owned(),
716 KclValue::String { .. } => "a string".to_owned(),
717 KclValue::Enum { value } => format!("a value of enum `{}`", value.enum_id().declared_name()),
718 KclValue::SketchVar { .. } => "a sketch variable".to_owned(),
719 KclValue::SketchConstraint { .. } => "a sketch constraint".to_owned(),
720 KclValue::Object { .. } => "an object".to_owned(),
721 KclValue::Module { .. } => "a module".to_owned(),
722 KclValue::Type { .. } => "a type".to_owned(),
723 KclValue::KclNone { .. } => "none".to_owned(),
724 KclValue::BoundedEdge { .. } => "a bounded edge".to_owned(),
725 KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } => {
726 if value.is_empty() {
727 "an empty array".to_owned()
728 } else {
729 const MAX: usize = 3;
731
732 let len = value.len();
733 let element_tys = value
734 .iter()
735 .take(MAX)
736 .map(|elem| elem.principal_type_string())
737 .collect::<Vec<_>>()
738 .join(", ");
739 let mut result = format!("an array of {element_tys}");
740 if len > MAX {
741 result.push_str(&format!(", ... with {len} values"));
742 }
743 if len == 1 {
744 result.push_str(" with 1 value");
745 }
746 result
747 }
748 }
749 }
750 }
751
752 pub(crate) fn from_sketch_var_literal(
753 literal: &Node<NumericLiteral>,
754 id: SketchVarId,
755 node_path: Option<crate::NodePath>,
756 exec_state: &ExecState,
757 ) -> Self {
758 let meta = vec![literal.metadata()];
759 let ty = NumericType::from_parsed(literal.suffix, &exec_state.mod_local.settings);
760 KclValue::SketchVar {
761 value: Box::new(SketchVar {
762 id,
763 initial_value: literal.value,
764 node_path,
765 meta,
766 ty,
767 }),
768 }
769 }
770
771 pub(crate) fn from_literal(literal: Node<Literal>, exec_state: &mut ExecState) -> Self {
772 let meta = vec![literal.metadata()];
773 match literal.inner.value {
774 LiteralValue::Number { value, suffix } => {
775 let ty = NumericType::from_parsed(suffix, &exec_state.mod_local.settings);
776 if let NumericType::Default { len, .. } = &ty
777 && !exec_state.mod_local.explicit_length_units
778 && *len != UnitLength::Millimeters
779 {
780 exec_state.warn(
781 CompilationIssue::err(
782 literal.as_source_range(),
783 "Project-wide units are deprecated. Prefer to use per-file default units.",
784 )
785 .with_suggestion(
786 "Fix by adding per-file settings",
787 format!("@{SETTINGS}({SETTINGS_UNIT_LENGTH} = {len})\n"),
788 Some(SourceRange::new(0, 0, literal.module_id)),
790 crate::errors::Tag::Deprecated,
791 ),
792 annotations::WARN_DEPRECATED,
793 );
794 }
795 KclValue::Number { value, meta, ty }
796 }
797 LiteralValue::String(value) => KclValue::String { value, meta },
798 LiteralValue::Bool(value) => KclValue::Bool { value, meta },
799 }
800 }
801
802 pub(crate) fn from_default_param(param: DefaultParamVal, exec_state: &mut ExecState) -> Self {
803 match param {
804 DefaultParamVal::Literal(lit) => Self::from_literal(lit, exec_state),
805 DefaultParamVal::KclNone(value) => KclValue::KclNone {
806 value,
807 meta: Default::default(),
808 },
809 }
810 }
811
812 pub(crate) fn map_env_ref(&self, old_env: EnvironmentRef, new_env: EnvironmentRef) -> Self {
813 let mut result = self.clone();
814 if let KclValue::Function { ref mut value, .. } = result
815 && let FunctionSource {
816 body: FunctionBody::Kcl(memory),
817 ..
818 } = &mut **value
819 {
820 memory.replace_env(old_env, new_env);
821 }
822
823 result
824 }
825
826 pub(crate) fn map_env_ref_and_epoch(&self, old_env: EnvironmentRef, new_env: EnvironmentRef) -> Self {
827 let mut result = self.clone();
828 if let KclValue::Function { ref mut value, .. } = result
829 && let FunctionSource {
830 body: FunctionBody::Kcl(memory),
831 ..
832 } = &mut **value
833 {
834 memory.replace_env_and_epoch(old_env, new_env);
835 }
836
837 result
838 }
839
840 pub const fn from_number_with_type(f: f64, ty: NumericType, meta: Vec<Metadata>) -> Self {
841 Self::Number { value: f, meta, ty }
842 }
843
844 pub fn from_point2d(p: [f64; 2], ty: NumericType, meta: Vec<Metadata>) -> Self {
846 let [x, y] = p;
847 Self::Tuple {
848 value: vec![
849 Self::Number {
850 value: x,
851 meta: meta.clone(),
852 ty,
853 },
854 Self::Number {
855 value: y,
856 meta: meta.clone(),
857 ty,
858 },
859 ],
860 meta,
861 }
862 }
863
864 pub fn from_point3d(p: [f64; 3], ty: NumericType, meta: Vec<Metadata>) -> Self {
866 let [x, y, z] = p;
867 Self::Tuple {
868 value: vec![
869 Self::Number {
870 value: x,
871 meta: meta.clone(),
872 ty,
873 },
874 Self::Number {
875 value: y,
876 meta: meta.clone(),
877 ty,
878 },
879 Self::Number {
880 value: z,
881 meta: meta.clone(),
882 ty,
883 },
884 ],
885 meta,
886 }
887 }
888
889 pub(crate) fn array_from_point2d(p: [f64; 2], ty: NumericType, meta: Vec<Metadata>) -> Self {
891 let [x, y] = p;
892 Self::HomArray {
893 value: vec![
894 Self::Number {
895 value: x,
896 meta: meta.clone(),
897 ty,
898 },
899 Self::Number { value: y, meta, ty },
900 ],
901 ty: ty.into(),
902 }
903 }
904
905 pub fn array_from_point3d(p: [f64; 3], ty: NumericType, meta: Vec<Metadata>) -> Self {
907 let [x, y, z] = p;
908 Self::HomArray {
909 value: vec![
910 Self::Number {
911 value: x,
912 meta: meta.clone(),
913 ty,
914 },
915 Self::Number {
916 value: y,
917 meta: meta.clone(),
918 ty,
919 },
920 Self::Number { value: z, meta, ty },
921 ],
922 ty: ty.into(),
923 }
924 }
925
926 pub(crate) fn from_unsolved_expr(expr: UnsolvedExpr, meta: Vec<Metadata>) -> Self {
927 match expr {
928 UnsolvedExpr::Known(v) => crate::execution::KclValue::Number {
929 value: v.n,
930 ty: v.ty,
931 meta,
932 },
933 UnsolvedExpr::Unknown(var_id) => crate::execution::KclValue::SketchVar {
937 value: Box::new(SketchVar {
938 id: var_id,
939 initial_value: Default::default(),
940 ty: Default::default(),
942 node_path: None,
943 meta,
944 }),
945 },
946 }
947 }
948
949 pub(crate) fn as_usize(&self) -> Option<usize> {
950 match self {
951 KclValue::Number { value, .. } => crate::try_f64_to_usize(*value),
952 _ => None,
953 }
954 }
955
956 pub fn as_int(&self) -> Option<i64> {
957 match self {
958 KclValue::Number { value, .. } => crate::try_f64_to_i64(*value),
959 _ => None,
960 }
961 }
962
963 pub fn as_int_with_ty(&self) -> Option<(i64, NumericType)> {
964 match self {
965 KclValue::Number { value, ty, .. } => crate::try_f64_to_i64(*value).map(|i| (i, *ty)),
966 _ => None,
967 }
968 }
969
970 pub fn as_object(&self) -> Option<&KclObjectFields> {
971 match self {
972 KclValue::Object { value, .. } => Some(value),
973 _ => None,
974 }
975 }
976
977 pub fn into_object(self) -> Option<KclObjectFields> {
978 match self {
979 KclValue::Object { value, .. } => Some(value),
980 _ => None,
981 }
982 }
983
984 pub fn as_unsolved_expr(&self) -> Option<UnsolvedExpr> {
985 match self {
986 KclValue::Number { value, ty, .. } => Some(UnsolvedExpr::Known(TyF64::new(*value, *ty))),
987 KclValue::SketchVar { value, .. } => Some(UnsolvedExpr::Unknown(value.id)),
988 _ => None,
989 }
990 }
991
992 pub fn to_sketch_expr(&self) -> Option<crate::front::Expr> {
993 match self {
994 KclValue::Number { value, ty, .. } => Some(crate::front::Expr::Number(crate::front::Number {
995 value: *value,
996 units: (*ty).try_into().ok()?,
997 })),
998 KclValue::SketchVar { value, .. } => Some(crate::front::Expr::Var(crate::front::Number {
999 value: value.initial_value,
1000 units: value.ty.try_into().ok()?,
1001 })),
1002 _ => None,
1003 }
1004 }
1005
1006 pub fn as_str(&self) -> Option<&str> {
1007 match self {
1008 KclValue::String { value, .. } => Some(value),
1009 _ => None,
1010 }
1011 }
1012
1013 pub fn into_array(self) -> Vec<KclValue> {
1014 match self {
1015 KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } => value,
1016 _ => vec![self],
1017 }
1018 }
1019
1020 pub fn as_slice(&self) -> Option<&[KclValue]> {
1021 match self {
1022 KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } => Some(value),
1023 _ => None,
1024 }
1025 }
1026
1027 pub fn as_point2d(&self) -> Option<[TyF64; 2]> {
1028 let value = match self {
1029 KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } => value,
1030 _ => return None,
1031 };
1032
1033 let [x, y] = value.as_slice() else {
1034 return None;
1035 };
1036 let x = x.as_ty_f64()?;
1037 let y = y.as_ty_f64()?;
1038 Some([x, y])
1039 }
1040
1041 pub fn as_point3d(&self) -> Option<[TyF64; 3]> {
1042 let value = match self {
1043 KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } => value,
1044 _ => return None,
1045 };
1046
1047 let [x, y, z] = value.as_slice() else {
1048 return None;
1049 };
1050 let x = x.as_ty_f64()?;
1051 let y = y.as_ty_f64()?;
1052 let z = z.as_ty_f64()?;
1053 Some([x, y, z])
1054 }
1055
1056 pub fn as_uuid(&self) -> Option<uuid::Uuid> {
1057 match self {
1058 KclValue::Uuid { value, .. } => Some(*value),
1059 _ => None,
1060 }
1061 }
1062
1063 pub fn as_plane(&self) -> Option<&Plane> {
1064 match self {
1065 KclValue::Plane { value, .. } => Some(value),
1066 _ => None,
1067 }
1068 }
1069
1070 pub fn as_solid(&self) -> Option<&Solid> {
1071 match self {
1072 KclValue::Solid { value, .. } => Some(value),
1073 _ => None,
1074 }
1075 }
1076
1077 pub fn as_sketch(&self) -> Option<&Sketch> {
1078 match self {
1079 KclValue::Sketch { value, .. } => Some(value),
1080 _ => None,
1081 }
1082 }
1083
1084 pub fn as_mut_sketch(&mut self) -> Option<&mut Sketch> {
1085 match self {
1086 KclValue::Sketch { value } => Some(value),
1087 _ => None,
1088 }
1089 }
1090
1091 pub fn as_sketch_var(&self) -> Option<&SketchVar> {
1092 match self {
1093 KclValue::SketchVar { value, .. } => Some(value),
1094 _ => None,
1095 }
1096 }
1097
1098 pub fn as_segment(&self) -> Option<&Segment> {
1100 match self {
1101 KclValue::Segment { value, .. } => match &value.repr {
1102 SegmentRepr::Solved { segment } => Some(segment),
1103 _ => None,
1104 },
1105 _ => None,
1106 }
1107 }
1108
1109 pub fn into_segment(self) -> Option<Segment> {
1111 match self {
1112 KclValue::Segment { value, .. } => match value.repr {
1113 SegmentRepr::Solved { segment } => Some(*segment),
1114 _ => None,
1115 },
1116 _ => None,
1117 }
1118 }
1119
1120 pub fn as_mut_tag(&mut self) -> Option<&mut TagIdentifier> {
1121 match self {
1122 KclValue::TagIdentifier(value) => Some(value),
1123 _ => None,
1124 }
1125 }
1126
1127 #[cfg(test)]
1128 pub fn as_f64(&self) -> Option<f64> {
1129 match self {
1130 KclValue::Number { value, .. } => Some(*value),
1131 _ => None,
1132 }
1133 }
1134
1135 pub fn as_ty_f64(&self) -> Option<TyF64> {
1136 match self {
1137 KclValue::Number { value, ty, .. } => Some(TyF64::new(*value, *ty)),
1138 _ => None,
1139 }
1140 }
1141
1142 pub fn as_bool(&self) -> Option<bool> {
1143 match self {
1144 KclValue::Bool { value, .. } => Some(*value),
1145 _ => None,
1146 }
1147 }
1148
1149 pub fn as_function(&self) -> Option<&FunctionSource> {
1151 match self {
1152 KclValue::Function { value, .. } => Some(value),
1153 _ => None,
1154 }
1155 }
1156
1157 pub fn get_tag_identifier(&self) -> Result<TagIdentifier, KclError> {
1159 match self {
1160 KclValue::TagIdentifier(t) => Ok(*t.clone()),
1161 _ => Err(KclError::new_semantic(KclErrorDetails::new(
1162 format!("Not a tag identifier: {self:?}"),
1163 self.clone().into(),
1164 ))),
1165 }
1166 }
1167
1168 pub fn get_tag_declarator(&self) -> Result<TagNode, KclError> {
1170 match self {
1171 KclValue::TagDeclarator(t) => Ok((**t).clone()),
1172 _ => Err(KclError::new_semantic(KclErrorDetails::new(
1173 format!("Not a tag declarator: {self:?}"),
1174 self.clone().into(),
1175 ))),
1176 }
1177 }
1178
1179 pub fn get_bool(&self) -> Result<bool, KclError> {
1181 self.as_bool().ok_or_else(|| {
1182 KclError::new_type(KclErrorDetails::new(
1183 format!("Expected bool, found {}", self.human_friendly_type()),
1184 self.into(),
1185 ))
1186 })
1187 }
1188
1189 pub fn is_unknown_number(&self) -> bool {
1190 match self {
1191 KclValue::Number { ty, .. } => !ty.is_fully_specified(),
1192 _ => false,
1193 }
1194 }
1195
1196 pub fn value_str(&self) -> Option<String> {
1197 match self {
1198 KclValue::Bool { value, .. } => Some(format!("{value}")),
1199 KclValue::Number { value, .. } => Some(format!("{value}")),
1201 KclValue::String { value, .. } => Some(format!("'{value}'")),
1202 KclValue::Enum { value } => Some(value.qualified_name()),
1203 KclValue::SketchVar { value, .. } => Some(format!("var {}", value.initial_value)),
1205 KclValue::Uuid { value, .. } => Some(format!("{value}")),
1206 KclValue::TagDeclarator(tag) => Some(format!("${}", tag.name)),
1207 KclValue::TagIdentifier(tag) => Some(format!("${}", tag.value)),
1208 KclValue::Tuple { .. } => Some("[...]".to_owned()),
1210 KclValue::HomArray { .. } => Some("[...]".to_owned()),
1211 KclValue::Object { .. } => Some("{ ... }".to_owned()),
1212 KclValue::Module { .. }
1213 | KclValue::GdtAnnotation { .. }
1214 | KclValue::SketchConstraint { .. }
1215 | KclValue::Solid { .. }
1216 | KclValue::Sketch { .. }
1217 | KclValue::Helix { .. }
1218 | KclValue::ImportedGeometry(_)
1219 | KclValue::Function { .. }
1220 | KclValue::Plane { .. }
1221 | KclValue::Face { .. }
1222 | KclValue::Segment { .. }
1223 | KclValue::KclNone { .. }
1224 | KclValue::BoundedEdge { .. }
1225 | KclValue::Type { .. } => None,
1226 }
1227 }
1228}
1229
1230impl From<Geometry> for KclValue {
1231 fn from(value: Geometry) -> Self {
1232 match value {
1233 Geometry::Sketch(x) => Self::Sketch { value: Box::new(x) },
1234 Geometry::Solid(x) => Self::Solid { value: Box::new(x) },
1235 }
1236 }
1237}
1238
1239impl From<GeometryWithImportedGeometry> for KclValue {
1240 fn from(value: GeometryWithImportedGeometry) -> Self {
1241 match value {
1242 GeometryWithImportedGeometry::Sketch(x) => Self::Sketch { value: Box::new(x) },
1243 GeometryWithImportedGeometry::Solid(x) => Self::Solid { value: Box::new(x) },
1244 GeometryWithImportedGeometry::ImportedGeometry(x) => Self::ImportedGeometry(*x),
1245 }
1246 }
1247}
1248
1249impl From<Vec<GeometryWithImportedGeometry>> for KclValue {
1250 fn from(mut values: Vec<GeometryWithImportedGeometry>) -> Self {
1251 if values.len() == 1
1252 && let Some(v) = values.pop()
1253 {
1254 KclValue::from(v)
1255 } else {
1256 KclValue::HomArray {
1257 value: values.into_iter().map(KclValue::from).collect(),
1258 ty: RuntimeType::Union(vec![
1259 RuntimeType::Primitive(PrimitiveType::Sketch),
1260 RuntimeType::Primitive(PrimitiveType::Solid),
1261 RuntimeType::Primitive(PrimitiveType::ImportedGeometry),
1262 ]),
1263 }
1264 }
1265 }
1266}
1267
1268#[cfg(test)]
1269mod tests {
1270 use super::*;
1271 use crate::exec::UnitType;
1272
1273 #[test]
1274 fn test_human_friendly_type() {
1275 let len = KclValue::Number {
1276 value: 1.0,
1277 ty: NumericType::Known(UnitType::GenericLength),
1278 meta: vec![],
1279 };
1280 assert_eq!(len.human_friendly_type(), "a number (Length)".to_string());
1281
1282 let unknown = KclValue::Number {
1283 value: 1.0,
1284 ty: NumericType::Unknown,
1285 meta: vec![],
1286 };
1287 assert_eq!(unknown.human_friendly_type(), "a number with unknown units".to_string());
1288
1289 let mm = KclValue::Number {
1290 value: 1.0,
1291 ty: NumericType::Known(UnitType::Length(UnitLength::Millimeters)),
1292 meta: vec![],
1293 };
1294 assert_eq!(mm.human_friendly_type(), "a number (mm)".to_string());
1295
1296 let array1_mm = KclValue::HomArray {
1297 value: vec![mm.clone()],
1298 ty: RuntimeType::any(),
1299 };
1300 assert_eq!(
1301 array1_mm.human_friendly_type(),
1302 "an array of `number(mm)` with 1 value".to_string()
1303 );
1304
1305 let array2_mm = KclValue::HomArray {
1306 value: vec![mm.clone(), mm.clone()],
1307 ty: RuntimeType::any(),
1308 };
1309 assert_eq!(
1310 array2_mm.human_friendly_type(),
1311 "an array of `number(mm)`, `number(mm)`".to_string()
1312 );
1313
1314 let array3_mm = KclValue::HomArray {
1315 value: vec![mm.clone(), mm.clone(), mm.clone()],
1316 ty: RuntimeType::any(),
1317 };
1318 assert_eq!(
1319 array3_mm.human_friendly_type(),
1320 "an array of `number(mm)`, `number(mm)`, `number(mm)`".to_string()
1321 );
1322
1323 let inches = KclValue::Number {
1324 value: 1.0,
1325 ty: NumericType::Known(UnitType::Length(UnitLength::Inches)),
1326 meta: vec![],
1327 };
1328 let array4 = KclValue::HomArray {
1329 value: vec![mm.clone(), mm.clone(), inches, mm],
1330 ty: RuntimeType::any(),
1331 };
1332 assert_eq!(
1333 array4.human_friendly_type(),
1334 "an array of `number(mm)`, `number(mm)`, `number(in)`, ... with 4 values".to_string()
1335 );
1336
1337 let empty_array = KclValue::HomArray {
1338 value: vec![],
1339 ty: RuntimeType::any(),
1340 };
1341 assert_eq!(empty_array.human_friendly_type(), "an empty array".to_string());
1342
1343 let array_nested = KclValue::HomArray {
1344 value: vec![array2_mm],
1345 ty: RuntimeType::any(),
1346 };
1347 assert_eq!(
1348 array_nested.human_friendly_type(),
1349 "an array of `[any; 2]` with 1 value".to_string()
1350 );
1351 }
1352
1353 fn color_def() -> Arc<EnumTypeDef> {
1354 Arc::new(
1355 EnumTypeDef::new(
1356 EnumTypeId::new(ModuleId::default(), "Color"),
1357 vec!["Red".to_owned(), "Green".to_owned()],
1358 )
1359 .unwrap(),
1360 )
1361 }
1362
1363 fn color_red() -> KclValue {
1364 KclValue::Enum {
1365 value: Box::new(EnumValue::new(color_def(), "Red", vec![])),
1366 }
1367 }
1368
1369 #[test]
1370 fn enum_values_describe_themselves_by_name_and_variant() {
1371 let red = color_red();
1372
1373 assert_eq!(red.human_friendly_type(), "a value of enum `Color`");
1374 assert_eq!(red.value_str(), Some("Color::Red".to_owned()));
1376 assert!(red.show_variable_in_feature_tree());
1377 }
1378
1379 #[test]
1383 fn enum_values_are_exposed_by_nominal_identity() {
1384 let view = crate::execution::KclValueView::from(color_red());
1385 assert_eq!(
1386 view,
1387 crate::execution::KclValueView::Enum {
1388 enum_name: "Color".to_owned(),
1389 variant: "Red".to_owned(),
1390 }
1391 );
1392
1393 let op = crate::execution::cad_op::op_from_kcl_value(&color_red());
1394 assert_eq!(
1395 op,
1396 kcl_api::OpKclValue::Enum {
1397 enum_name: "Color".to_owned(),
1398 variant: "Red".to_owned(),
1399 }
1400 );
1401 }
1402
1403 #[test]
1408 fn enum_values_serialize_as_identity_and_variant() {
1409 assert_eq!(
1410 serde_json::to_value(color_red()).unwrap(),
1411 serde_json::json!({
1412 "type": "Enum",
1413 "value": {
1414 "enum_id": { "module_id": 0, "declared_name": "Color" },
1415 "variant": "Red",
1416 },
1417 })
1418 );
1419 }
1420
1421 #[test]
1422 fn enum_declarations_carry_their_variants() {
1423 let def = EnumTypeDef::new(
1424 EnumTypeId::new(ModuleId::default(), "Color"),
1425 vec!["Red".to_owned(), "Green".to_owned()],
1426 )
1427 .unwrap();
1428
1429 assert_eq!(def.variants(), ["Red", "Green"]);
1430 assert!(def.has_variant("Red"));
1431 assert!(!def.has_variant("Blue"));
1432 assert_ne!(
1435 def.id(),
1436 EnumTypeDef::new(
1437 EnumTypeId::new(ModuleId::from_usize(1), "Color"),
1438 vec!["Red".to_owned(), "Green".to_owned()],
1439 )
1440 .unwrap()
1441 .id()
1442 );
1443 }
1444
1445 #[test]
1446 fn enum_rejects_duplicate_variant() {
1447 let err = EnumTypeDef::new(
1448 EnumTypeId::new(ModuleId::default(), "Color"),
1449 vec!["Red".to_owned(), "Green".to_owned(), "Red".to_owned()],
1450 )
1451 .unwrap_err();
1452
1453 assert_eq!(
1454 err,
1455 DuplicateVariant {
1456 name: "Red".to_owned(),
1457 first_index: 0,
1458 duplicate_index: 2,
1459 }
1460 );
1461 }
1462
1463 #[test]
1464 fn enum_reports_earliest_duplicate() {
1465 let err = EnumTypeDef::new(
1468 EnumTypeId::new(ModuleId::default(), "Color"),
1469 vec![
1470 "Red".to_owned(),
1471 "Green".to_owned(),
1472 "Blue".to_owned(),
1473 "Green".to_owned(),
1474 "Red".to_owned(),
1475 ],
1476 )
1477 .unwrap_err();
1478
1479 assert_eq!(err.name, "Green");
1480 assert_eq!(err.first_index, 1);
1481 assert_eq!(err.duplicate_index, 3);
1482 }
1483}