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::CameraView;
18use crate::execution::EnvironmentRef;
19use crate::execution::ExecState;
20use crate::execution::Face;
21use crate::execution::GdtAnnotation;
22use crate::execution::Geometry;
23use crate::execution::GeometryWithImportedGeometry;
24use crate::execution::Helix;
25use crate::execution::ImportedGeometry;
26use crate::execution::Metadata;
27use crate::execution::NamedViewValue;
28use crate::execution::Plane;
29use crate::execution::Segment;
30use crate::execution::SegmentRepr;
31use crate::execution::Sketch;
32use crate::execution::SketchConstraint;
33use crate::execution::SketchVar;
34use crate::execution::SketchVarId;
35use crate::execution::Solid;
36use crate::execution::TagIdentifier;
37use crate::execution::UnsolvedExpr;
38use crate::execution::annotations::FnAttrs;
39use crate::execution::annotations::SETTINGS;
40use crate::execution::annotations::SETTINGS_UNIT_LENGTH;
41use crate::execution::annotations::VersionConstraint;
42use crate::execution::annotations::{self};
43use crate::execution::types::NumericType;
44use crate::execution::types::NumericTypeExt;
45use crate::execution::types::PrimitiveType;
46use crate::execution::types::RuntimeType;
47use crate::parsing::ast::types::BoxNode;
48use crate::parsing::ast::types::DefaultParamVal;
49use crate::parsing::ast::types::FunctionExpression;
50use crate::parsing::ast::types::KclNone;
51use crate::parsing::ast::types::Literal;
52use crate::parsing::ast::types::LiteralValue;
53use crate::parsing::ast::types::Node;
54use crate::parsing::ast::types::NumericLiteral;
55use crate::parsing::ast::types::TagDeclarator;
56use crate::parsing::ast::types::TagNode;
57use crate::parsing::ast::types::Type;
58use crate::std::StdFnProps;
59use crate::std::args::TyF64;
60
61pub type KclObjectFields = HashMap<String, KclValue>;
62
63#[derive(Debug, Clone, Default, PartialEq, Serialize)]
64pub enum KclObjectKind {
65 #[default]
66 Default,
67 SketchTags {
68 #[serde(default, skip_serializing_if = "Vec::is_empty")]
69 deprecated_solid_tag_names: Vec<String>,
70 },
71}
72
73impl KclObjectKind {
74 pub(crate) fn is_default(&self) -> bool {
75 match self {
76 KclObjectKind::Default => true,
77 KclObjectKind::SketchTags { .. } => false,
78 }
79 }
80
81 pub(crate) fn deprecated_solid_tag_names(&self) -> &[String] {
82 match self {
83 Self::Default => &[],
84 Self::SketchTags {
85 deprecated_solid_tag_names,
86 } => deprecated_solid_tag_names,
87 }
88 }
89}
90
91#[derive(Debug, Clone, Serialize, PartialEq)]
93#[serde(tag = "type")]
94pub enum KclValue {
95 Uuid {
96 value: ::uuid::Uuid,
97 #[serde(skip)]
98 meta: Vec<Metadata>,
99 },
100 Bool {
101 value: bool,
102 #[serde(skip)]
103 meta: Vec<Metadata>,
104 },
105 Number {
106 value: f64,
107 ty: NumericType,
108 #[serde(skip)]
109 meta: Vec<Metadata>,
110 },
111 String {
112 value: String,
113 #[serde(skip)]
114 meta: Vec<Metadata>,
115 },
116 Enum {
117 value: Box<EnumValue>,
118 },
119 SketchVar {
120 value: Box<SketchVar>,
121 },
122 SketchConstraint {
123 value: Box<SketchConstraint>,
124 },
125 Tuple {
126 value: Vec<KclValue>,
127 #[serde(skip)]
128 meta: Vec<Metadata>,
129 },
130 HomArray {
132 value: Vec<KclValue>,
133 #[serde(skip)]
135 ty: RuntimeType,
136 },
137 Object {
138 value: KclObjectFields,
139 constrainable: bool,
140 #[serde(default, skip_serializing_if = "KclObjectKind::is_default")]
141 object_kind: KclObjectKind,
142 #[serde(skip)]
143 meta: Vec<Metadata>,
144 },
145 TagIdentifier(Box<TagIdentifier>),
146 TagDeclarator(BoxNode<TagDeclarator>),
147 GdtAnnotation {
148 value: Box<GdtAnnotation>,
149 },
150 Plane {
151 value: Box<Plane>,
152 },
153 Face {
154 value: Box<Face>,
155 },
156 BoundedEdge {
157 value: BoundedEdge,
158 meta: Vec<Metadata>,
159 },
160 Segment {
161 value: Box<AbstractSegment>,
162 },
163 Sketch {
164 value: Box<Sketch>,
165 },
166 Solid {
167 value: Box<Solid>,
168 },
169 Helix {
170 value: Box<Helix>,
171 },
172 CameraView {
173 value: Box<CameraView>,
174 },
175 NamedView {
176 value: Box<NamedViewValue>,
177 },
178 ImportedGeometry(ImportedGeometry),
179 Function {
180 #[serde(serialize_with = "function_value_stub")]
181 value: Box<FunctionSource>,
182 #[serde(skip)]
183 meta: Vec<Metadata>,
184 },
185 Module {
186 value: ModuleId,
187 #[serde(skip)]
188 meta: Vec<Metadata>,
189 },
190 Type {
191 #[serde(skip)]
192 value: TypeDef,
193 experimental: bool,
194 #[serde(skip)]
195 meta: Vec<Metadata>,
196 },
197 KclNone {
198 value: KclNone,
199 #[serde(skip)]
200 meta: Vec<Metadata>,
201 },
202}
203
204fn function_value_stub<S>(_value: &FunctionSource, serializer: S) -> Result<S::Ok, S::Error>
205where
206 S: serde::Serializer,
207{
208 serializer.serialize_unit()
209}
210
211#[derive(Debug, Clone, PartialEq)]
212pub struct NamedParam {
213 pub experimental: bool,
214 pub added_in: Option<VersionConstraint>,
217 pub deprecated: bool,
219 pub deprecated_since: Option<VersionConstraint>,
221 pub removed_in: Option<VersionConstraint>,
224 pub default_value: Option<DefaultParamVal>,
225 pub ty: Option<Type>,
226 pub resolved_ty: Option<RuntimeType>,
231}
232
233#[derive(Debug, Clone, Copy, PartialEq, Eq)]
239pub(crate) enum ParamUnavailable<'a> {
240 NotYetAdded(&'a VersionConstraint),
243 Removed(&'a VersionConstraint),
246}
247
248impl NamedParam {
249 pub(crate) fn unavailable_reason(&self, exec_state: &ExecState) -> Option<ParamUnavailable<'_>> {
253 let version = exec_state.kcl_version().as_str();
254 if let Some(added) = &self.added_in
255 && !crate::execution::annotations::version_ge(version, added)
256 {
257 return Some(ParamUnavailable::NotYetAdded(added));
258 }
259 if let Some(removed) = &self.removed_in
260 && crate::execution::annotations::version_ge(version, removed)
261 {
262 return Some(ParamUnavailable::Removed(removed));
263 }
264 None
265 }
266
267 pub(crate) fn is_available(&self, exec_state: &ExecState) -> bool {
270 self.unavailable_reason(exec_state).is_none()
271 }
272}
273
274#[derive(Debug, Clone, PartialEq)]
275pub struct FunctionSource {
276 pub input_arg: Option<(String, Option<Type>)>,
277 pub resolved_input_ty: Option<RuntimeType>,
282 pub named_args: IndexMap<String, NamedParam>,
283 pub return_type: Option<Node<Type>>,
284 pub resolved_return_ty: Option<RuntimeType>,
288 pub deprecated: bool,
289 pub deprecated_since: Option<VersionConstraint>,
293 pub experimental: bool,
294 pub include_in_feature_tree: bool,
295 pub std_props: Option<StdFnProps>,
296 pub body: FunctionBody,
297 pub ast: BoxNode<FunctionExpression>,
298}
299
300pub struct KclFunctionSourceParams {
301 pub std_props: Option<StdFnProps>,
302 pub experimental: bool,
303 pub include_in_feature_tree: bool,
304}
305
306impl FunctionSource {
307 pub fn rust(func: crate::std::StdFn, ast: BoxNode<FunctionExpression>, props: StdFnProps, attrs: FnAttrs) -> Self {
308 let (input_arg, named_args) = Self::args_from_ast(&ast);
309
310 FunctionSource {
311 input_arg,
312 resolved_input_ty: None,
313 named_args,
314 return_type: ast.return_type.clone(),
315 resolved_return_ty: None,
316 deprecated: attrs.deprecated,
317 deprecated_since: attrs.deprecated_since,
318 experimental: attrs.experimental,
319 include_in_feature_tree: attrs.include_in_feature_tree,
320 std_props: Some(props),
321 body: FunctionBody::Rust(func),
322 ast,
323 }
324 }
325
326 pub fn kcl(ast: BoxNode<FunctionExpression>, memory: EnvironmentRef, params: KclFunctionSourceParams) -> Self {
327 let KclFunctionSourceParams {
328 std_props,
329 experimental,
330 include_in_feature_tree,
331 } = params;
332 let (input_arg, named_args) = Self::args_from_ast(&ast);
333 FunctionSource {
334 input_arg,
335 resolved_input_ty: None,
336 named_args,
337 return_type: ast.return_type.clone(),
338 resolved_return_ty: None,
339 deprecated: false,
340 deprecated_since: None,
341 experimental,
342 include_in_feature_tree,
343 std_props,
344 body: FunctionBody::Kcl(memory),
345 ast,
346 }
347 }
348
349 #[expect(clippy::type_complexity)]
350 fn args_from_ast(ast: &FunctionExpression) -> (Option<(String, Option<Type>)>, IndexMap<String, NamedParam>) {
351 let mut input_arg = None;
352 let mut named_args = IndexMap::new();
353 for p in &ast.params {
354 if !p.labeled {
355 input_arg = Some((
356 p.identifier.name.clone(),
357 p.param_type.as_ref().map(|t| t.inner.clone()),
358 ));
359 continue;
360 }
361
362 named_args.insert(
363 p.identifier.name.clone(),
364 NamedParam {
365 experimental: p.experimental,
366 added_in: p.added_in.clone(),
367 deprecated: p.deprecated,
368 deprecated_since: p.deprecated_since.clone(),
369 removed_in: p.removed_in.clone(),
370 default_value: p.default_value.clone(),
371 ty: p.param_type.as_ref().map(|t| t.inner.clone()),
372 resolved_ty: None,
373 },
374 );
375 }
376
377 (input_arg, named_args)
378 }
379
380 #[doc(hidden)]
381 pub fn is_std(&self) -> bool {
382 self.std_props.is_some()
383 }
384
385 pub(crate) fn active_named_arg<'a>(&'a self, label: &str, exec_state: &ExecState) -> Option<&'a NamedParam> {
390 self.named_args
391 .get(label)
392 .filter(|param| param.is_available(exec_state))
393 }
394
395 pub(crate) fn active_named_args<'a>(
399 &'a self,
400 exec_state: &'a ExecState,
401 ) -> impl Iterator<Item = (&'a String, &'a NamedParam)> + 'a {
402 self.named_args
403 .iter()
404 .filter(move |(_, param)| param.is_available(exec_state))
405 }
406
407 pub(crate) fn resolve_signature_types(&mut self, exec_state: &mut ExecState) -> Result<(), KclError> {
418 for param in &self.ast.params {
419 let Some(ty) = ¶m.param_type else {
420 continue;
421 };
422 let resolved = RuntimeType::from_parsed(ty.inner.clone(), exec_state, ty.as_source_range(), false, false)
423 .map_err(|e| KclError::new_semantic(e.into()))?;
424 if param.labeled {
425 if let Some(named) = self.named_args.get_mut(¶m.identifier.name) {
426 named.resolved_ty = Some(resolved);
427 }
428 } else {
429 self.resolved_input_ty = Some(resolved);
430 }
431 }
432
433 if let Some(ret_ty) = &self.return_type {
434 self.resolved_return_ty = Some(
435 RuntimeType::from_parsed(ret_ty.inner.clone(), exec_state, ret_ty.as_source_range(), false, false)
436 .map_err(|e| KclError::new_semantic(e.into()))?,
437 );
438 }
439
440 Ok(())
441 }
442}
443
444#[derive(Debug, Clone, PartialEq)]
445#[allow(unpredictable_function_pointer_comparisons)]
448pub enum FunctionBody {
449 Rust(crate::std::StdFn),
450 Kcl(EnvironmentRef),
451}
452
453#[derive(Debug, Clone, PartialEq)]
454pub enum TypeDef {
455 RustRepr(PrimitiveType, StdFnProps),
456 Alias(RuntimeType),
457 Enum(Arc<EnumTypeDef>),
461}
462
463#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
471pub struct EnumTypeId {
472 module_id: ModuleId,
473 declared_name: String,
474}
475
476impl EnumTypeId {
477 pub fn new(module_id: ModuleId, declared_name: impl Into<String>) -> Self {
478 Self {
479 module_id,
480 declared_name: declared_name.into(),
481 }
482 }
483
484 pub fn module_id(&self) -> ModuleId {
485 self.module_id
486 }
487
488 pub fn declared_name(&self) -> &str {
491 &self.declared_name
492 }
493}
494
495#[derive(Debug, Clone, PartialEq)]
497pub struct EnumTypeDef {
498 id: EnumTypeId,
499 variants: Vec<String>,
500}
501
502#[derive(Debug, Clone, PartialEq)]
510pub struct DuplicateVariant {
511 pub name: String,
513 pub first_index: usize,
515 pub duplicate_index: usize,
517}
518
519impl EnumTypeDef {
520 pub fn new(id: EnumTypeId, variants: Vec<String>) -> Result<Self, DuplicateVariant> {
527 for (duplicate_index, variant) in variants.iter().enumerate() {
528 if let Some(first_index) = variants[..duplicate_index].iter().position(|v| v == variant) {
529 return Err(DuplicateVariant {
530 name: variant.clone(),
531 first_index,
532 duplicate_index,
533 });
534 }
535 }
536
537 Ok(Self { id, variants })
538 }
539
540 pub fn id(&self) -> &EnumTypeId {
541 &self.id
542 }
543
544 pub fn variants(&self) -> &[String] {
545 &self.variants
546 }
547
548 pub fn has_variant(&self, name: &str) -> bool {
549 self.variants.iter().any(|v| v == name)
550 }
551}
552
553#[derive(Debug, Clone, Serialize)]
564pub struct EnumValue {
565 #[serde(rename = "enum_id", serialize_with = "serialize_enum_def_id")]
569 def: Arc<EnumTypeDef>,
570 variant: String,
571 #[serde(skip)]
572 meta: Vec<Metadata>,
573}
574
575fn serialize_enum_def_id<S: Serializer>(def: &Arc<EnumTypeDef>, serializer: S) -> Result<S::Ok, S::Error> {
576 def.id().serialize(serializer)
577}
578
579impl PartialEq for EnumValue {
584 fn eq(&self, other: &Self) -> bool {
585 self.def.id() == other.def.id() && self.variant == other.variant
586 }
587}
588
589impl EnumValue {
590 pub fn new(def: Arc<EnumTypeDef>, variant: impl Into<String>, meta: Vec<Metadata>) -> Self {
591 Self {
592 def,
593 variant: variant.into(),
594 meta,
595 }
596 }
597
598 pub fn enum_id(&self) -> &EnumTypeId {
599 self.def.id()
600 }
601
602 pub fn variant(&self) -> &str {
603 &self.variant
604 }
605
606 pub fn meta(&self) -> &[Metadata] {
607 &self.meta
608 }
609
610 pub fn declared_string_repr(&self) -> String {
618 self.variant.clone()
619 }
620
621 pub fn qualified_name(&self) -> String {
623 format!("{}::{}", self.def.id().declared_name(), self.variant)
624 }
625}
626
627impl From<Vec<GdtAnnotation>> for KclValue {
628 fn from(mut values: Vec<GdtAnnotation>) -> Self {
629 if values.len() == 1 {
630 let value = values.pop().expect("Just checked len == 1");
631 KclValue::GdtAnnotation { value: Box::new(value) }
632 } else {
633 KclValue::HomArray {
634 value: values
635 .into_iter()
636 .map(|s| KclValue::GdtAnnotation { value: Box::new(s) })
637 .collect(),
638 ty: RuntimeType::Primitive(PrimitiveType::GdtAnnotation),
639 }
640 }
641 }
642}
643
644impl From<Vec<Sketch>> for KclValue {
645 fn from(mut eg: Vec<Sketch>) -> Self {
646 if eg.len() == 1
647 && let Some(s) = eg.pop()
648 {
649 KclValue::Sketch { value: Box::new(s) }
650 } else {
651 KclValue::HomArray {
652 value: eg
653 .into_iter()
654 .map(|s| KclValue::Sketch { value: Box::new(s) })
655 .collect(),
656 ty: RuntimeType::Primitive(PrimitiveType::Sketch),
657 }
658 }
659 }
660}
661
662impl From<Vec<Solid>> for KclValue {
663 fn from(mut eg: Vec<Solid>) -> Self {
664 if eg.len() == 1
665 && let Some(s) = eg.pop()
666 {
667 KclValue::Solid { value: Box::new(s) }
668 } else {
669 KclValue::HomArray {
670 value: eg.into_iter().map(|s| KclValue::Solid { value: Box::new(s) }).collect(),
671 ty: RuntimeType::Primitive(PrimitiveType::Solid),
672 }
673 }
674 }
675}
676
677impl From<KclValue> for Vec<SourceRange> {
678 fn from(item: KclValue) -> Self {
679 match item {
680 KclValue::TagDeclarator(t) => vec![SourceRange::new(t.start, t.end, t.module_id)],
681 KclValue::TagIdentifier(t) => to_vec_sr(&t.meta),
682 KclValue::GdtAnnotation { value } => to_vec_sr(&value.meta),
683 KclValue::Solid { value } => to_vec_sr(&value.meta),
684 KclValue::Sketch { value } => to_vec_sr(&value.meta),
685 KclValue::Helix { value } => to_vec_sr(&value.meta),
686 KclValue::CameraView { value } => to_vec_sr(value.meta()),
687 KclValue::NamedView { value } => to_vec_sr(value.meta()),
688 KclValue::ImportedGeometry(i) => to_vec_sr(&i.meta),
689 KclValue::Function { meta, .. } => to_vec_sr(&meta),
690 KclValue::Plane { value } => to_vec_sr(&value.meta),
691 KclValue::Face { value } => to_vec_sr(&value.meta),
692 KclValue::Segment { value } => to_vec_sr(&value.meta),
693 KclValue::Bool { meta, .. } => to_vec_sr(&meta),
694 KclValue::Number { meta, .. } => to_vec_sr(&meta),
695 KclValue::String { meta, .. } => to_vec_sr(&meta),
696 KclValue::Enum { value } => to_vec_sr(value.meta()),
697 KclValue::SketchVar { value, .. } => to_vec_sr(&value.meta),
698 KclValue::SketchConstraint { value, .. } => to_vec_sr(&value.meta),
699 KclValue::Tuple { meta, .. } => to_vec_sr(&meta),
700 KclValue::HomArray { value, .. } => value.iter().flat_map(Into::<Vec<SourceRange>>::into).collect(),
701 KclValue::Object { meta, .. } => to_vec_sr(&meta),
702 KclValue::Module { meta, .. } => to_vec_sr(&meta),
703 KclValue::Uuid { meta, .. } => to_vec_sr(&meta),
704 KclValue::Type { meta, .. } => to_vec_sr(&meta),
705 KclValue::KclNone { meta, .. } => to_vec_sr(&meta),
706 KclValue::BoundedEdge { meta, .. } => to_vec_sr(&meta),
707 }
708 }
709}
710
711fn to_vec_sr(meta: &[Metadata]) -> Vec<SourceRange> {
712 meta.iter().map(|m| m.source_range).collect()
713}
714
715impl From<&KclValue> for Vec<SourceRange> {
716 fn from(item: &KclValue) -> Self {
717 match item {
718 KclValue::TagDeclarator(t) => vec![SourceRange::new(t.start, t.end, t.module_id)],
719 KclValue::TagIdentifier(t) => to_vec_sr(&t.meta),
720 KclValue::GdtAnnotation { value } => to_vec_sr(&value.meta),
721 KclValue::Solid { value } => to_vec_sr(&value.meta),
722 KclValue::Sketch { value } => to_vec_sr(&value.meta),
723 KclValue::Helix { value } => to_vec_sr(&value.meta),
724 KclValue::CameraView { value } => to_vec_sr(value.meta()),
725 KclValue::NamedView { value } => to_vec_sr(value.meta()),
726 KclValue::ImportedGeometry(i) => to_vec_sr(&i.meta),
727 KclValue::Function { meta, .. } => to_vec_sr(meta),
728 KclValue::Plane { value } => to_vec_sr(&value.meta),
729 KclValue::Face { value } => to_vec_sr(&value.meta),
730 KclValue::Segment { value } => to_vec_sr(&value.meta),
731 KclValue::Bool { meta, .. } => to_vec_sr(meta),
732 KclValue::Number { meta, .. } => to_vec_sr(meta),
733 KclValue::String { meta, .. } => to_vec_sr(meta),
734 KclValue::Enum { value } => to_vec_sr(value.meta()),
735 KclValue::SketchVar { value, .. } => to_vec_sr(&value.meta),
736 KclValue::SketchConstraint { value, .. } => to_vec_sr(&value.meta),
737 KclValue::Uuid { meta, .. } => to_vec_sr(meta),
738 KclValue::Tuple { meta, .. } => to_vec_sr(meta),
739 KclValue::HomArray { value, .. } => value.iter().flat_map(Into::<Vec<SourceRange>>::into).collect(),
740 KclValue::Object { meta, .. } => to_vec_sr(meta),
741 KclValue::Module { meta, .. } => to_vec_sr(meta),
742 KclValue::KclNone { meta, .. } => to_vec_sr(meta),
743 KclValue::Type { meta, .. } => to_vec_sr(meta),
744 KclValue::BoundedEdge { meta, .. } => to_vec_sr(meta),
745 }
746 }
747}
748
749impl From<&KclValue> for SourceRange {
750 fn from(item: &KclValue) -> Self {
751 let v: Vec<_> = item.into();
752 v.into_iter().next().unwrap_or_default()
753 }
754}
755
756impl KclValue {
757 pub(crate) fn metadata(&self) -> Vec<Metadata> {
758 match self {
759 KclValue::Uuid { value: _, meta } => meta.clone(),
760 KclValue::Bool { value: _, meta } => meta.clone(),
761 KclValue::Number { meta, .. } => meta.clone(),
762 KclValue::String { value: _, meta } => meta.clone(),
763 KclValue::Enum { value } => value.meta().to_vec(),
764 KclValue::SketchVar { value, .. } => value.meta.clone(),
765 KclValue::SketchConstraint { value, .. } => value.meta.clone(),
766 KclValue::Tuple { value: _, meta } => meta.clone(),
767 KclValue::HomArray { value, .. } => value.iter().flat_map(|v| v.metadata()).collect(),
768 KclValue::Object { meta, .. } => meta.clone(),
769 KclValue::TagIdentifier(x) => x.meta.clone(),
770 KclValue::TagDeclarator(x) => vec![x.metadata()],
771 KclValue::GdtAnnotation { value } => value.meta.clone(),
772 KclValue::Plane { value } => value.meta.clone(),
773 KclValue::Face { value } => value.meta.clone(),
774 KclValue::Segment { value } => value.meta.clone(),
775 KclValue::Sketch { value } => value.meta.clone(),
776 KclValue::Solid { value } => value.meta.clone(),
777 KclValue::Helix { value } => value.meta.clone(),
778 KclValue::CameraView { value } => value.meta().to_vec(),
779 KclValue::NamedView { value } => value.meta().to_vec(),
780 KclValue::ImportedGeometry(x) => x.meta.clone(),
781 KclValue::Function { meta, .. } => meta.clone(),
782 KclValue::Module { meta, .. } => meta.clone(),
783 KclValue::KclNone { meta, .. } => meta.clone(),
784 KclValue::Type { meta, .. } => meta.clone(),
785 KclValue::BoundedEdge { meta, .. } => meta.clone(),
786 }
787 }
788
789 #[allow(unused)]
790 pub(crate) fn none() -> Self {
791 Self::KclNone {
792 value: Default::default(),
793 meta: Default::default(),
794 }
795 }
796
797 pub(crate) fn show_variable_in_feature_tree(&self) -> bool {
801 match self {
802 KclValue::Uuid { .. } => false,
803 KclValue::Bool { .. } | KclValue::Number { .. } | KclValue::String { .. } | KclValue::Enum { .. } => true,
804 KclValue::SketchVar { .. }
805 | KclValue::SketchConstraint { .. }
806 | KclValue::Tuple { .. }
807 | KclValue::HomArray { .. }
808 | KclValue::Object { .. }
809 | KclValue::TagIdentifier(_)
810 | KclValue::TagDeclarator(_)
811 | KclValue::GdtAnnotation { .. }
812 | KclValue::Plane { .. }
813 | KclValue::Face { .. }
814 | KclValue::Segment { .. }
815 | KclValue::Sketch { .. }
816 | KclValue::Solid { .. }
817 | KclValue::Helix { .. }
818 | KclValue::CameraView { .. }
819 | KclValue::NamedView { .. }
820 | KclValue::ImportedGeometry(_)
821 | KclValue::Function { .. }
822 | KclValue::Module { .. }
823 | KclValue::Type { .. }
824 | KclValue::BoundedEdge { .. }
825 | KclValue::KclNone { .. } => false,
826 }
827 }
828
829 pub(crate) fn human_friendly_type(&self) -> String {
832 match self {
833 KclValue::Uuid { .. } => "a unique ID (uuid)".to_owned(),
834 KclValue::TagDeclarator(_) => "a tag declarator".to_owned(),
835 KclValue::TagIdentifier(_) => "a tag identifier".to_owned(),
836 KclValue::GdtAnnotation { .. } => "an annotation".to_owned(),
837 KclValue::Solid { .. } => "a solid".to_owned(),
838 KclValue::Sketch { .. } => "a sketch".to_owned(),
839 KclValue::Helix { .. } => "a helix".to_owned(),
840 KclValue::CameraView { .. } => "a camera view".to_owned(),
841 KclValue::NamedView { .. } => "a named view".to_owned(),
842 KclValue::ImportedGeometry(_) => "an imported geometry".to_owned(),
843 KclValue::Function { .. } => "a function".to_owned(),
844 KclValue::Plane { .. } => "a plane".to_owned(),
845 KclValue::Face { .. } => "a face".to_owned(),
846 KclValue::Segment { .. } => "a segment".to_owned(),
847 KclValue::Bool { .. } => "a boolean (`true` or `false`)".to_owned(),
848 KclValue::Number {
849 ty: NumericType::Unknown,
850 ..
851 } => "a number with unknown units".to_owned(),
852 KclValue::Number {
853 ty: NumericType::Known(units),
854 ..
855 } => format!("a number ({units})"),
856 KclValue::Number { .. } => "a number".to_owned(),
857 KclValue::String { .. } => "a string".to_owned(),
858 KclValue::Enum { value } => format!("a value of enum `{}`", value.enum_id().declared_name()),
859 KclValue::SketchVar { .. } => "a sketch variable".to_owned(),
860 KclValue::SketchConstraint { .. } => "a sketch constraint".to_owned(),
861 KclValue::Object { .. } => "an object".to_owned(),
862 KclValue::Module { .. } => "a module".to_owned(),
863 KclValue::Type { .. } => "a type".to_owned(),
864 KclValue::KclNone { .. } => "none".to_owned(),
865 KclValue::BoundedEdge { .. } => "a bounded edge".to_owned(),
866 KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } => {
867 if value.is_empty() {
868 "an empty array".to_owned()
869 } else {
870 const MAX: usize = 3;
872
873 let len = value.len();
874 let element_tys = value
875 .iter()
876 .take(MAX)
877 .map(|elem| elem.principal_type_string())
878 .collect::<Vec<_>>()
879 .join(", ");
880 let mut result = format!("an array of {element_tys}");
881 if len > MAX {
882 result.push_str(&format!(", ... with {len} values"));
883 }
884 if len == 1 {
885 result.push_str(" with 1 value");
886 }
887 result
888 }
889 }
890 }
891 }
892
893 pub(crate) fn from_sketch_var_literal(
894 literal: &Node<NumericLiteral>,
895 id: SketchVarId,
896 node_path: Option<crate::NodePath>,
897 exec_state: &ExecState,
898 ) -> Self {
899 let meta = vec![literal.metadata()];
900 let ty = NumericType::from_parsed(literal.suffix, &exec_state.mod_local.settings);
901 KclValue::SketchVar {
902 value: Box::new(SketchVar {
903 id,
904 initial_value: literal.value,
905 node_path,
906 meta,
907 ty,
908 }),
909 }
910 }
911
912 pub(crate) fn from_literal(literal: Node<Literal>, exec_state: &mut ExecState) -> Self {
913 let meta = vec![literal.metadata()];
914 match literal.inner.value {
915 LiteralValue::Number { value, suffix } => {
916 let ty = NumericType::from_parsed(suffix, &exec_state.mod_local.settings);
917 if let NumericType::Default { len, .. } = &ty
918 && !exec_state.mod_local.explicit_length_units
919 && *len != UnitLength::Millimeters
920 {
921 exec_state.warn(
922 CompilationIssue::err(
923 literal.as_source_range(),
924 "Project-wide units are deprecated. Prefer to use per-file default units.",
925 )
926 .with_suggestion(
927 "Fix by adding per-file settings",
928 format!("@{SETTINGS}({SETTINGS_UNIT_LENGTH} = {len})\n"),
929 Some(SourceRange::new(0, 0, literal.module_id)),
931 crate::errors::Tag::Deprecated,
932 ),
933 annotations::WARN_DEPRECATED,
934 );
935 }
936 KclValue::Number { value, meta, ty }
937 }
938 LiteralValue::String(value) => KclValue::String { value, meta },
939 LiteralValue::Bool(value) => KclValue::Bool { value, meta },
940 }
941 }
942
943 pub(crate) fn from_default_param(param: DefaultParamVal, exec_state: &mut ExecState) -> Self {
944 match param {
945 DefaultParamVal::Literal(lit) => Self::from_literal(lit, exec_state),
946 DefaultParamVal::KclNone(value) => KclValue::KclNone {
947 value,
948 meta: Default::default(),
949 },
950 }
951 }
952
953 pub(crate) fn map_env_ref(&self, old_env: EnvironmentRef, new_env: EnvironmentRef) -> Self {
954 let mut result = self.clone();
955 if let KclValue::Function { ref mut value, .. } = result
956 && let FunctionSource {
957 body: FunctionBody::Kcl(memory),
958 ..
959 } = &mut **value
960 {
961 memory.replace_env(old_env, new_env);
962 }
963
964 result
965 }
966
967 pub(crate) fn map_env_ref_and_epoch(&self, old_env: EnvironmentRef, new_env: EnvironmentRef) -> Self {
968 let mut result = self.clone();
969 if let KclValue::Function { ref mut value, .. } = result
970 && let FunctionSource {
971 body: FunctionBody::Kcl(memory),
972 ..
973 } = &mut **value
974 {
975 memory.replace_env_and_epoch(old_env, new_env);
976 }
977
978 result
979 }
980
981 pub const fn from_number_with_type(f: f64, ty: NumericType, meta: Vec<Metadata>) -> Self {
982 Self::Number { value: f, meta, ty }
983 }
984
985 pub fn from_point2d(p: [f64; 2], ty: NumericType, meta: Vec<Metadata>) -> Self {
987 let [x, y] = p;
988 Self::Tuple {
989 value: vec![
990 Self::Number {
991 value: x,
992 meta: meta.clone(),
993 ty,
994 },
995 Self::Number {
996 value: y,
997 meta: meta.clone(),
998 ty,
999 },
1000 ],
1001 meta,
1002 }
1003 }
1004
1005 pub fn from_imported_geometries(geometries: Vec<ImportedGeometry>) -> Self {
1006 geometries
1007 .into_iter()
1008 .map(|geometry| GeometryWithImportedGeometry::ImportedGeometry(Box::new(geometry)))
1009 .collect::<Vec<_>>()
1010 .into()
1011 }
1012
1013 pub fn from_point3d(p: [f64; 3], ty: NumericType, meta: Vec<Metadata>) -> Self {
1015 let [x, y, z] = p;
1016 Self::Tuple {
1017 value: vec![
1018 Self::Number {
1019 value: x,
1020 meta: meta.clone(),
1021 ty,
1022 },
1023 Self::Number {
1024 value: y,
1025 meta: meta.clone(),
1026 ty,
1027 },
1028 Self::Number {
1029 value: z,
1030 meta: meta.clone(),
1031 ty,
1032 },
1033 ],
1034 meta,
1035 }
1036 }
1037
1038 pub(crate) fn array_from_point2d(p: [f64; 2], ty: NumericType, meta: Vec<Metadata>) -> Self {
1040 let [x, y] = p;
1041 Self::HomArray {
1042 value: vec![
1043 Self::Number {
1044 value: x,
1045 meta: meta.clone(),
1046 ty,
1047 },
1048 Self::Number { value: y, meta, ty },
1049 ],
1050 ty: ty.into(),
1051 }
1052 }
1053
1054 pub fn array_from_point3d(p: [f64; 3], ty: NumericType, meta: Vec<Metadata>) -> Self {
1056 let [x, y, z] = p;
1057 Self::HomArray {
1058 value: vec![
1059 Self::Number {
1060 value: x,
1061 meta: meta.clone(),
1062 ty,
1063 },
1064 Self::Number {
1065 value: y,
1066 meta: meta.clone(),
1067 ty,
1068 },
1069 Self::Number { value: z, meta, ty },
1070 ],
1071 ty: ty.into(),
1072 }
1073 }
1074
1075 pub(crate) fn from_unsolved_expr(expr: UnsolvedExpr, meta: Vec<Metadata>) -> Self {
1076 match expr {
1077 UnsolvedExpr::Known(v) => crate::execution::KclValue::Number {
1078 value: v.n,
1079 ty: v.ty,
1080 meta,
1081 },
1082 UnsolvedExpr::Unknown(var_id) => crate::execution::KclValue::SketchVar {
1086 value: Box::new(SketchVar {
1087 id: var_id,
1088 initial_value: Default::default(),
1089 ty: Default::default(),
1091 node_path: None,
1092 meta,
1093 }),
1094 },
1095 }
1096 }
1097
1098 pub(crate) fn as_usize(&self) -> Option<usize> {
1099 match self {
1100 KclValue::Number { value, .. } => crate::try_f64_to_usize(*value),
1101 _ => None,
1102 }
1103 }
1104
1105 pub fn as_int(&self) -> Option<i64> {
1106 match self {
1107 KclValue::Number { value, .. } => crate::try_f64_to_i64(*value),
1108 _ => None,
1109 }
1110 }
1111
1112 pub fn as_int_with_ty(&self) -> Option<(i64, NumericType)> {
1113 match self {
1114 KclValue::Number { value, ty, .. } => crate::try_f64_to_i64(*value).map(|i| (i, *ty)),
1115 _ => None,
1116 }
1117 }
1118
1119 pub fn as_object(&self) -> Option<&KclObjectFields> {
1120 match self {
1121 KclValue::Object { value, .. } => Some(value),
1122 _ => None,
1123 }
1124 }
1125
1126 pub fn into_object(self) -> Option<KclObjectFields> {
1127 match self {
1128 KclValue::Object { value, .. } => Some(value),
1129 _ => None,
1130 }
1131 }
1132
1133 pub fn as_unsolved_expr(&self) -> Option<UnsolvedExpr> {
1134 match self {
1135 KclValue::Number { value, ty, .. } => Some(UnsolvedExpr::Known(TyF64::new(*value, *ty))),
1136 KclValue::SketchVar { value, .. } => Some(UnsolvedExpr::Unknown(value.id)),
1137 _ => None,
1138 }
1139 }
1140
1141 pub fn to_sketch_expr(&self) -> Option<crate::front::Expr> {
1142 match self {
1143 KclValue::Number { value, ty, .. } => Some(crate::front::Expr::Number(crate::front::Number {
1144 value: *value,
1145 units: (*ty).try_into().ok()?,
1146 })),
1147 KclValue::SketchVar { value, .. } => Some(crate::front::Expr::Var(crate::front::Number {
1148 value: value.initial_value,
1149 units: value.ty.try_into().ok()?,
1150 })),
1151 _ => None,
1152 }
1153 }
1154
1155 pub fn as_str(&self) -> Option<&str> {
1156 match self {
1157 KclValue::String { value, .. } => Some(value),
1158 _ => None,
1159 }
1160 }
1161
1162 pub fn into_array(self) -> Vec<KclValue> {
1163 match self {
1164 KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } => value,
1165 _ => vec![self],
1166 }
1167 }
1168
1169 pub fn as_slice(&self) -> Option<&[KclValue]> {
1170 match self {
1171 KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } => Some(value),
1172 _ => None,
1173 }
1174 }
1175
1176 pub fn as_point2d(&self) -> Option<[TyF64; 2]> {
1177 let value = match self {
1178 KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } => value,
1179 _ => return None,
1180 };
1181
1182 let [x, y] = value.as_slice() else {
1183 return None;
1184 };
1185 let x = x.as_ty_f64()?;
1186 let y = y.as_ty_f64()?;
1187 Some([x, y])
1188 }
1189
1190 pub fn as_point3d(&self) -> Option<[TyF64; 3]> {
1191 let value = match self {
1192 KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } => value,
1193 _ => return None,
1194 };
1195
1196 let [x, y, z] = value.as_slice() else {
1197 return None;
1198 };
1199 let x = x.as_ty_f64()?;
1200 let y = y.as_ty_f64()?;
1201 let z = z.as_ty_f64()?;
1202 Some([x, y, z])
1203 }
1204
1205 pub fn as_uuid(&self) -> Option<uuid::Uuid> {
1206 match self {
1207 KclValue::Uuid { value, .. } => Some(*value),
1208 _ => None,
1209 }
1210 }
1211
1212 pub fn as_plane(&self) -> Option<&Plane> {
1213 match self {
1214 KclValue::Plane { value, .. } => Some(value),
1215 _ => None,
1216 }
1217 }
1218
1219 pub fn as_solid(&self) -> Option<&Solid> {
1220 match self {
1221 KclValue::Solid { value, .. } => Some(value),
1222 _ => None,
1223 }
1224 }
1225
1226 pub fn as_sketch(&self) -> Option<&Sketch> {
1227 match self {
1228 KclValue::Sketch { value, .. } => Some(value),
1229 _ => None,
1230 }
1231 }
1232
1233 pub fn as_mut_sketch(&mut self) -> Option<&mut Sketch> {
1234 match self {
1235 KclValue::Sketch { value } => Some(value),
1236 _ => None,
1237 }
1238 }
1239
1240 pub fn as_sketch_var(&self) -> Option<&SketchVar> {
1241 match self {
1242 KclValue::SketchVar { value, .. } => Some(value),
1243 _ => None,
1244 }
1245 }
1246
1247 pub fn as_segment(&self) -> Option<&Segment> {
1249 match self {
1250 KclValue::Segment { value, .. } => match &value.repr {
1251 SegmentRepr::Solved { segment } => Some(segment),
1252 _ => None,
1253 },
1254 _ => None,
1255 }
1256 }
1257
1258 pub fn into_segment(self) -> Option<Segment> {
1260 match self {
1261 KclValue::Segment { value, .. } => match value.repr {
1262 SegmentRepr::Solved { segment } => Some(*segment),
1263 _ => None,
1264 },
1265 _ => None,
1266 }
1267 }
1268
1269 pub fn as_mut_tag(&mut self) -> Option<&mut TagIdentifier> {
1270 match self {
1271 KclValue::TagIdentifier(value) => Some(value),
1272 _ => None,
1273 }
1274 }
1275
1276 #[cfg(test)]
1277 pub fn as_f64(&self) -> Option<f64> {
1278 match self {
1279 KclValue::Number { value, .. } => Some(*value),
1280 _ => None,
1281 }
1282 }
1283
1284 pub fn as_ty_f64(&self) -> Option<TyF64> {
1285 match self {
1286 KclValue::Number { value, ty, .. } => Some(TyF64::new(*value, *ty)),
1287 _ => None,
1288 }
1289 }
1290
1291 pub fn as_bool(&self) -> Option<bool> {
1292 match self {
1293 KclValue::Bool { value, .. } => Some(*value),
1294 _ => None,
1295 }
1296 }
1297
1298 pub fn as_function(&self) -> Option<&FunctionSource> {
1300 match self {
1301 KclValue::Function { value, .. } => Some(value),
1302 _ => None,
1303 }
1304 }
1305
1306 pub fn get_tag_identifier(&self) -> Result<TagIdentifier, KclError> {
1308 match self {
1309 KclValue::TagIdentifier(t) => Ok(*t.clone()),
1310 _ => Err(KclError::new_semantic(KclErrorDetails::new(
1311 format!("Not a tag identifier: {self:?}"),
1312 self.clone().into(),
1313 ))),
1314 }
1315 }
1316
1317 pub fn get_tag_declarator(&self) -> Result<TagNode, KclError> {
1319 match self {
1320 KclValue::TagDeclarator(t) => Ok((**t).clone()),
1321 _ => Err(KclError::new_semantic(KclErrorDetails::new(
1322 format!("Not a tag declarator: {self:?}"),
1323 self.clone().into(),
1324 ))),
1325 }
1326 }
1327
1328 pub fn get_bool(&self) -> Result<bool, KclError> {
1330 self.as_bool().ok_or_else(|| {
1331 KclError::new_type(KclErrorDetails::new(
1332 format!("Expected bool, found {}", self.human_friendly_type()),
1333 self.into(),
1334 ))
1335 })
1336 }
1337
1338 pub fn is_unknown_number(&self) -> bool {
1339 match self {
1340 KclValue::Number { ty, .. } => !ty.is_fully_specified(),
1341 _ => false,
1342 }
1343 }
1344
1345 pub fn value_str(&self) -> Option<String> {
1346 match self {
1347 KclValue::Bool { value, .. } => Some(format!("{value}")),
1348 KclValue::Number { value, .. } => Some(format!("{value}")),
1350 KclValue::String { value, .. } => Some(format!("'{value}'")),
1351 KclValue::Enum { value } => Some(value.qualified_name()),
1352 KclValue::SketchVar { value, .. } => Some(format!("var {}", value.initial_value)),
1354 KclValue::Uuid { value, .. } => Some(format!("{value}")),
1355 KclValue::TagDeclarator(tag) => Some(format!("${}", tag.name)),
1356 KclValue::TagIdentifier(tag) => Some(format!("${}", tag.value)),
1357 KclValue::Tuple { .. } => Some("[...]".to_owned()),
1359 KclValue::HomArray { .. } => Some("[...]".to_owned()),
1360 KclValue::Object { .. } => Some("{ ... }".to_owned()),
1361 KclValue::Module { .. }
1362 | KclValue::GdtAnnotation { .. }
1363 | KclValue::SketchConstraint { .. }
1364 | KclValue::Solid { .. }
1365 | KclValue::Sketch { .. }
1366 | KclValue::Helix { .. }
1367 | KclValue::CameraView { .. }
1368 | KclValue::NamedView { .. }
1369 | KclValue::ImportedGeometry(_)
1370 | KclValue::Function { .. }
1371 | KclValue::Plane { .. }
1372 | KclValue::Face { .. }
1373 | KclValue::Segment { .. }
1374 | KclValue::KclNone { .. }
1375 | KclValue::BoundedEdge { .. }
1376 | KclValue::Type { .. } => None,
1377 }
1378 }
1379}
1380
1381impl From<Geometry> for KclValue {
1382 fn from(value: Geometry) -> Self {
1383 match value {
1384 Geometry::Sketch(x) => Self::Sketch { value: Box::new(x) },
1385 Geometry::Solid(x) => Self::Solid { value: Box::new(x) },
1386 }
1387 }
1388}
1389
1390impl From<GeometryWithImportedGeometry> for KclValue {
1391 fn from(value: GeometryWithImportedGeometry) -> Self {
1392 match value {
1393 GeometryWithImportedGeometry::Sketch(x) => Self::Sketch { value: Box::new(x) },
1394 GeometryWithImportedGeometry::Solid(x) => Self::Solid { value: Box::new(x) },
1395 GeometryWithImportedGeometry::ImportedGeometry(x) => Self::ImportedGeometry(*x),
1396 }
1397 }
1398}
1399
1400impl From<Vec<GeometryWithImportedGeometry>> for KclValue {
1401 fn from(mut values: Vec<GeometryWithImportedGeometry>) -> Self {
1402 if values.len() == 1
1403 && let Some(v) = values.pop()
1404 {
1405 KclValue::from(v)
1406 } else {
1407 KclValue::HomArray {
1408 value: values.into_iter().map(KclValue::from).collect(),
1409 ty: RuntimeType::Union(vec![
1410 RuntimeType::Primitive(PrimitiveType::Sketch),
1411 RuntimeType::Primitive(PrimitiveType::Solid),
1412 RuntimeType::Primitive(PrimitiveType::ImportedGeometry),
1413 ]),
1414 }
1415 }
1416 }
1417}
1418
1419#[cfg(test)]
1420mod tests {
1421 use super::*;
1422 use crate::exec::UnitType;
1423
1424 #[test]
1425 fn tag_declaration_bindings_do_not_overwrite_each_other() {
1426 use kcl_api::TagDeclaratorView;
1427 use ts_rs::TS;
1428
1429 for ast_first in [true, false] {
1432 let output = tempfile::tempdir().unwrap();
1433 let config = ts_rs::Config::default().with_out_dir(output.path());
1434 if ast_first {
1435 TagDeclarator::export_all(&config).unwrap();
1436 kcl_api::BasePathView::export_all(&config).unwrap();
1437 } else {
1438 kcl_api::BasePathView::export_all(&config).unwrap();
1439 TagDeclarator::export_all(&config).unwrap();
1440 }
1441
1442 for (path, expected) in [
1443 (
1444 TagDeclarator::output_path().unwrap(),
1445 TagDeclarator::export_to_string(&config).unwrap(),
1446 ),
1447 (
1448 TagDeclaratorView::output_path().unwrap(),
1449 TagDeclaratorView::export_to_string(&config).unwrap(),
1450 ),
1451 ] {
1452 assert_eq!(std::fs::read_to_string(output.path().join(path)).unwrap(), expected);
1453 }
1454 }
1455 }
1456
1457 #[test]
1458 fn test_human_friendly_type() {
1459 let len = KclValue::Number {
1460 value: 1.0,
1461 ty: NumericType::Known(UnitType::GenericLength),
1462 meta: vec![],
1463 };
1464 assert_eq!(len.human_friendly_type(), "a number (Length)".to_string());
1465
1466 let unknown = KclValue::Number {
1467 value: 1.0,
1468 ty: NumericType::Unknown,
1469 meta: vec![],
1470 };
1471 assert_eq!(unknown.human_friendly_type(), "a number with unknown units".to_string());
1472
1473 let mm = KclValue::Number {
1474 value: 1.0,
1475 ty: NumericType::Known(UnitType::Length(UnitLength::Millimeters)),
1476 meta: vec![],
1477 };
1478 assert_eq!(mm.human_friendly_type(), "a number (mm)".to_string());
1479
1480 let array1_mm = KclValue::HomArray {
1481 value: vec![mm.clone()],
1482 ty: RuntimeType::any(),
1483 };
1484 assert_eq!(
1485 array1_mm.human_friendly_type(),
1486 "an array of `number(mm)` with 1 value".to_string()
1487 );
1488
1489 let array2_mm = KclValue::HomArray {
1490 value: vec![mm.clone(), mm.clone()],
1491 ty: RuntimeType::any(),
1492 };
1493 assert_eq!(
1494 array2_mm.human_friendly_type(),
1495 "an array of `number(mm)`, `number(mm)`".to_string()
1496 );
1497
1498 let array3_mm = KclValue::HomArray {
1499 value: vec![mm.clone(), mm.clone(), mm.clone()],
1500 ty: RuntimeType::any(),
1501 };
1502 assert_eq!(
1503 array3_mm.human_friendly_type(),
1504 "an array of `number(mm)`, `number(mm)`, `number(mm)`".to_string()
1505 );
1506
1507 let inches = KclValue::Number {
1508 value: 1.0,
1509 ty: NumericType::Known(UnitType::Length(UnitLength::Inches)),
1510 meta: vec![],
1511 };
1512 let array4 = KclValue::HomArray {
1513 value: vec![mm.clone(), mm.clone(), inches, mm],
1514 ty: RuntimeType::any(),
1515 };
1516 assert_eq!(
1517 array4.human_friendly_type(),
1518 "an array of `number(mm)`, `number(mm)`, `number(in)`, ... with 4 values".to_string()
1519 );
1520
1521 let empty_array = KclValue::HomArray {
1522 value: vec![],
1523 ty: RuntimeType::any(),
1524 };
1525 assert_eq!(empty_array.human_friendly_type(), "an empty array".to_string());
1526
1527 let array_nested = KclValue::HomArray {
1528 value: vec![array2_mm],
1529 ty: RuntimeType::any(),
1530 };
1531 assert_eq!(
1532 array_nested.human_friendly_type(),
1533 "an array of `[any; 2]` with 1 value".to_string()
1534 );
1535 }
1536
1537 fn color_def() -> Arc<EnumTypeDef> {
1538 Arc::new(
1539 EnumTypeDef::new(
1540 EnumTypeId::new(ModuleId::default(), "Color"),
1541 vec!["Red".to_owned(), "Green".to_owned()],
1542 )
1543 .unwrap(),
1544 )
1545 }
1546
1547 fn color_red() -> KclValue {
1548 KclValue::Enum {
1549 value: Box::new(EnumValue::new(color_def(), "Red", vec![])),
1550 }
1551 }
1552
1553 #[test]
1554 fn enum_values_describe_themselves_by_name_and_variant() {
1555 let red = color_red();
1556
1557 assert_eq!(red.human_friendly_type(), "a value of enum `Color`");
1558 assert_eq!(red.value_str(), Some("Color::Red".to_owned()));
1560 assert!(red.show_variable_in_feature_tree());
1561 }
1562
1563 #[test]
1567 fn enum_values_are_exposed_by_nominal_identity() {
1568 let view = crate::execution::KclValueView::from(color_red());
1569 assert_eq!(
1570 view,
1571 crate::execution::KclValueView::Enum {
1572 enum_name: "Color".to_owned(),
1573 variant: "Red".to_owned(),
1574 }
1575 );
1576
1577 let op = crate::execution::cad_op::op_from_kcl_value(&color_red());
1578 assert_eq!(
1579 op,
1580 kcl_api::OpKclValue::Enum {
1581 enum_name: "Color".to_owned(),
1582 variant: "Red".to_owned(),
1583 }
1584 );
1585 }
1586
1587 #[test]
1592 fn enum_values_serialize_as_identity_and_variant() {
1593 assert_eq!(
1594 serde_json::to_value(color_red()).unwrap(),
1595 serde_json::json!({
1596 "type": "Enum",
1597 "value": {
1598 "enum_id": { "module_id": 0, "declared_name": "Color" },
1599 "variant": "Red",
1600 },
1601 })
1602 );
1603 }
1604
1605 #[test]
1606 fn enum_declarations_carry_their_variants() {
1607 let def = EnumTypeDef::new(
1608 EnumTypeId::new(ModuleId::default(), "Color"),
1609 vec!["Red".to_owned(), "Green".to_owned()],
1610 )
1611 .unwrap();
1612
1613 assert_eq!(def.variants(), ["Red", "Green"]);
1614 assert!(def.has_variant("Red"));
1615 assert!(!def.has_variant("Blue"));
1616 assert_ne!(
1619 def.id(),
1620 EnumTypeDef::new(
1621 EnumTypeId::new(ModuleId::from_usize(1), "Color"),
1622 vec!["Red".to_owned(), "Green".to_owned()],
1623 )
1624 .unwrap()
1625 .id()
1626 );
1627 }
1628
1629 #[test]
1630 fn enum_rejects_duplicate_variant() {
1631 let err = EnumTypeDef::new(
1632 EnumTypeId::new(ModuleId::default(), "Color"),
1633 vec!["Red".to_owned(), "Green".to_owned(), "Red".to_owned()],
1634 )
1635 .unwrap_err();
1636
1637 assert_eq!(
1638 err,
1639 DuplicateVariant {
1640 name: "Red".to_owned(),
1641 first_index: 0,
1642 duplicate_index: 2,
1643 }
1644 );
1645 }
1646
1647 #[test]
1648 fn enum_reports_earliest_duplicate() {
1649 let err = EnumTypeDef::new(
1652 EnumTypeId::new(ModuleId::default(), "Color"),
1653 vec![
1654 "Red".to_owned(),
1655 "Green".to_owned(),
1656 "Blue".to_owned(),
1657 "Green".to_owned(),
1658 "Red".to_owned(),
1659 ],
1660 )
1661 .unwrap_err();
1662
1663 assert_eq!(err.name, "Green");
1664 assert_eq!(err.first_index, 1);
1665 assert_eq!(err.duplicate_index, 3);
1666 }
1667}