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