1use std::collections::HashMap;
2
3use anyhow::Result;
4use schemars::JsonSchema;
5use serde::Serialize;
6
7use super::types::UnitType;
8use crate::{
9 errors::KclErrorDetails,
10 execution::{
11 annotations::{SETTINGS, SETTINGS_UNIT_LENGTH},
12 types::{NumericType, PrimitiveType, RuntimeType, UnitLen},
13 EnvironmentRef, ExecState, Face, Geometry, GeometryWithImportedGeometry, Helix, ImportedGeometry, MetaSettings,
14 Metadata, Plane, Sketch, Solid, TagIdentifier,
15 },
16 parsing::ast::types::{
17 DefaultParamVal, FunctionExpression, KclNone, Literal, LiteralValue, Node, TagDeclarator, TagNode,
18 },
19 std::{args::TyF64, StdFnProps},
20 CompilationError, KclError, ModuleId, SourceRange,
21};
22
23pub type KclObjectFields = HashMap<String, KclValue>;
24
25#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
27#[ts(export)]
28#[serde(tag = "type")]
29pub enum KclValue {
30 Uuid {
31 value: ::uuid::Uuid,
32 #[serde(skip)]
33 meta: Vec<Metadata>,
34 },
35 Bool {
36 value: bool,
37 #[serde(skip)]
38 meta: Vec<Metadata>,
39 },
40 Number {
41 value: f64,
42 ty: NumericType,
43 #[serde(skip)]
44 meta: Vec<Metadata>,
45 },
46 String {
47 value: String,
48 #[serde(skip)]
49 meta: Vec<Metadata>,
50 },
51 Tuple {
52 value: Vec<KclValue>,
53 #[serde(skip)]
54 meta: Vec<Metadata>,
55 },
56 HomArray {
58 value: Vec<KclValue>,
59 #[serde(skip)]
61 ty: RuntimeType,
62 },
63 Object {
64 value: KclObjectFields,
65 #[serde(skip)]
66 meta: Vec<Metadata>,
67 },
68 TagIdentifier(Box<TagIdentifier>),
69 TagDeclarator(crate::parsing::ast::types::BoxNode<TagDeclarator>),
70 Plane {
71 value: Box<Plane>,
72 },
73 Face {
74 value: Box<Face>,
75 },
76 Sketch {
77 value: Box<Sketch>,
78 },
79 Solid {
80 value: Box<Solid>,
81 },
82 Helix {
83 value: Box<Helix>,
84 },
85 ImportedGeometry(ImportedGeometry),
86 Function {
87 #[serde(serialize_with = "function_value_stub")]
88 #[ts(type = "null")]
89 value: FunctionSource,
90 #[serde(skip)]
91 meta: Vec<Metadata>,
92 },
93 Module {
94 value: ModuleId,
95 #[serde(skip)]
96 meta: Vec<Metadata>,
97 },
98 #[ts(skip)]
99 Type {
100 #[serde(skip)]
101 value: TypeDef,
102 #[serde(skip)]
103 meta: Vec<Metadata>,
104 },
105 KclNone {
106 value: KclNone,
107 #[serde(skip)]
108 meta: Vec<Metadata>,
109 },
110}
111
112fn function_value_stub<S>(_value: &FunctionSource, serializer: S) -> Result<S::Ok, S::Error>
113where
114 S: serde::Serializer,
115{
116 serializer.serialize_unit()
117}
118
119#[derive(Debug, Clone, PartialEq, Default)]
120pub enum FunctionSource {
121 #[default]
122 None,
123 Std {
124 func: crate::std::StdFn,
125 ast: crate::parsing::ast::types::BoxNode<FunctionExpression>,
126 props: StdFnProps,
127 },
128 User {
129 ast: crate::parsing::ast::types::BoxNode<FunctionExpression>,
130 settings: MetaSettings,
131 memory: EnvironmentRef,
132 },
133}
134
135impl JsonSchema for FunctionSource {
136 fn schema_name() -> String {
137 "FunctionSource".to_owned()
138 }
139
140 fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
141 gen.subschema_for::<()>()
143 }
144}
145
146#[derive(Debug, Clone, PartialEq)]
147pub enum TypeDef {
148 RustRepr(PrimitiveType, StdFnProps),
149 Alias(RuntimeType),
150}
151
152impl From<Vec<Sketch>> for KclValue {
153 fn from(mut eg: Vec<Sketch>) -> Self {
154 if eg.len() == 1 {
155 KclValue::Sketch {
156 value: Box::new(eg.pop().unwrap()),
157 }
158 } else {
159 KclValue::HomArray {
160 value: eg
161 .into_iter()
162 .map(|s| KclValue::Sketch { value: Box::new(s) })
163 .collect(),
164 ty: RuntimeType::Primitive(PrimitiveType::Sketch),
165 }
166 }
167 }
168}
169
170impl From<Vec<Solid>> for KclValue {
171 fn from(mut eg: Vec<Solid>) -> Self {
172 if eg.len() == 1 {
173 KclValue::Solid {
174 value: Box::new(eg.pop().unwrap()),
175 }
176 } else {
177 KclValue::HomArray {
178 value: eg.into_iter().map(|s| KclValue::Solid { value: Box::new(s) }).collect(),
179 ty: RuntimeType::Primitive(PrimitiveType::Solid),
180 }
181 }
182 }
183}
184
185impl From<KclValue> for Vec<SourceRange> {
186 fn from(item: KclValue) -> Self {
187 match item {
188 KclValue::TagDeclarator(t) => vec![SourceRange::new(t.start, t.end, t.module_id)],
189 KclValue::TagIdentifier(t) => to_vec_sr(&t.meta),
190 KclValue::Solid { value } => to_vec_sr(&value.meta),
191 KclValue::Sketch { value } => to_vec_sr(&value.meta),
192 KclValue::Helix { value } => to_vec_sr(&value.meta),
193 KclValue::ImportedGeometry(i) => to_vec_sr(&i.meta),
194 KclValue::Function { meta, .. } => to_vec_sr(&meta),
195 KclValue::Plane { value } => to_vec_sr(&value.meta),
196 KclValue::Face { value } => to_vec_sr(&value.meta),
197 KclValue::Bool { meta, .. } => to_vec_sr(&meta),
198 KclValue::Number { meta, .. } => to_vec_sr(&meta),
199 KclValue::String { meta, .. } => to_vec_sr(&meta),
200 KclValue::Tuple { meta, .. } => to_vec_sr(&meta),
201 KclValue::HomArray { value, .. } => value.iter().flat_map(Into::<Vec<SourceRange>>::into).collect(),
202 KclValue::Object { meta, .. } => to_vec_sr(&meta),
203 KclValue::Module { meta, .. } => to_vec_sr(&meta),
204 KclValue::Uuid { meta, .. } => to_vec_sr(&meta),
205 KclValue::Type { meta, .. } => to_vec_sr(&meta),
206 KclValue::KclNone { meta, .. } => to_vec_sr(&meta),
207 }
208 }
209}
210
211fn to_vec_sr(meta: &[Metadata]) -> Vec<SourceRange> {
212 meta.iter().map(|m| m.source_range).collect()
213}
214
215impl From<&KclValue> for Vec<SourceRange> {
216 fn from(item: &KclValue) -> Self {
217 match item {
218 KclValue::TagDeclarator(t) => vec![SourceRange::new(t.start, t.end, t.module_id)],
219 KclValue::TagIdentifier(t) => to_vec_sr(&t.meta),
220 KclValue::Solid { value } => to_vec_sr(&value.meta),
221 KclValue::Sketch { value } => to_vec_sr(&value.meta),
222 KclValue::Helix { value } => to_vec_sr(&value.meta),
223 KclValue::ImportedGeometry(i) => to_vec_sr(&i.meta),
224 KclValue::Function { meta, .. } => to_vec_sr(meta),
225 KclValue::Plane { value } => to_vec_sr(&value.meta),
226 KclValue::Face { value } => to_vec_sr(&value.meta),
227 KclValue::Bool { meta, .. } => to_vec_sr(meta),
228 KclValue::Number { meta, .. } => to_vec_sr(meta),
229 KclValue::String { meta, .. } => to_vec_sr(meta),
230 KclValue::Uuid { meta, .. } => to_vec_sr(meta),
231 KclValue::Tuple { meta, .. } => to_vec_sr(meta),
232 KclValue::HomArray { value, .. } => value.iter().flat_map(Into::<Vec<SourceRange>>::into).collect(),
233 KclValue::Object { meta, .. } => to_vec_sr(meta),
234 KclValue::Module { meta, .. } => to_vec_sr(meta),
235 KclValue::KclNone { meta, .. } => to_vec_sr(meta),
236 KclValue::Type { meta, .. } => to_vec_sr(meta),
237 }
238 }
239}
240
241impl From<&KclValue> for SourceRange {
242 fn from(item: &KclValue) -> Self {
243 let v: Vec<_> = item.into();
244 v.into_iter().next().unwrap_or_default()
245 }
246}
247
248impl KclValue {
249 pub(crate) fn metadata(&self) -> Vec<Metadata> {
250 match self {
251 KclValue::Uuid { value: _, meta } => meta.clone(),
252 KclValue::Bool { value: _, meta } => meta.clone(),
253 KclValue::Number { meta, .. } => meta.clone(),
254 KclValue::String { value: _, meta } => meta.clone(),
255 KclValue::Tuple { value: _, meta } => meta.clone(),
256 KclValue::HomArray { value, .. } => value.iter().flat_map(|v| v.metadata()).collect(),
257 KclValue::Object { value: _, meta } => meta.clone(),
258 KclValue::TagIdentifier(x) => x.meta.clone(),
259 KclValue::TagDeclarator(x) => vec![x.metadata()],
260 KclValue::Plane { value } => value.meta.clone(),
261 KclValue::Face { value } => value.meta.clone(),
262 KclValue::Sketch { value } => value.meta.clone(),
263 KclValue::Solid { value } => value.meta.clone(),
264 KclValue::Helix { value } => value.meta.clone(),
265 KclValue::ImportedGeometry(x) => x.meta.clone(),
266 KclValue::Function { meta, .. } => meta.clone(),
267 KclValue::Module { meta, .. } => meta.clone(),
268 KclValue::KclNone { meta, .. } => meta.clone(),
269 KclValue::Type { meta, .. } => meta.clone(),
270 }
271 }
272
273 #[allow(unused)]
274 pub(crate) fn none() -> Self {
275 Self::KclNone {
276 value: Default::default(),
277 meta: Default::default(),
278 }
279 }
280
281 pub(crate) fn human_friendly_type(&self) -> String {
284 if let Some(t) = self.principal_type() {
285 return t.to_string();
286 }
287 match self {
288 KclValue::Uuid { .. } => "Unique ID (uuid)",
289 KclValue::TagDeclarator(_) => "TagDeclarator",
290 KclValue::TagIdentifier(_) => "TagIdentifier",
291 KclValue::Solid { .. } => "Solid",
292 KclValue::Sketch { .. } => "Sketch",
293 KclValue::Helix { .. } => "Helix",
294 KclValue::ImportedGeometry(_) => "ImportedGeometry",
295 KclValue::Function { .. } => "Function",
296 KclValue::Plane { .. } => "Plane",
297 KclValue::Face { .. } => "Face",
298 KclValue::Bool { .. } => "boolean (true/false value)",
299 KclValue::Number {
300 ty: NumericType::Unknown,
301 ..
302 } => "number(unknown units)",
303 KclValue::Number {
304 ty: NumericType::Known(UnitType::Length(_)),
305 ..
306 } => "number(Length)",
307 KclValue::Number {
308 ty: NumericType::Known(UnitType::Angle(_)),
309 ..
310 } => "number(Angle)",
311 KclValue::Number { .. } => "number",
312 KclValue::String { .. } => "string (text)",
313 KclValue::Tuple { .. } => "tuple (list)",
314 KclValue::HomArray { .. } => "array (list)",
315 KclValue::Object { .. } => "object",
316 KclValue::Module { .. } => "module",
317 KclValue::Type { .. } => "type",
318 KclValue::KclNone { .. } => "None",
319 }
320 .to_owned()
321 }
322
323 pub(crate) fn from_literal(literal: Node<Literal>, exec_state: &mut ExecState) -> Self {
324 let meta = vec![literal.metadata()];
325 match literal.inner.value {
326 LiteralValue::Number { value, suffix } => {
327 let ty = NumericType::from_parsed(suffix, &exec_state.mod_local.settings);
328 if let NumericType::Default { len, .. } = &ty {
329 if !exec_state.mod_local.explicit_length_units && *len != UnitLen::Mm {
330 exec_state.warn(
331 CompilationError::err(
332 literal.as_source_range(),
333 "Project-wide units are deprecated. Prefer to use per-file default units.",
334 )
335 .with_suggestion(
336 "Fix by adding per-file settings",
337 format!("@{SETTINGS}({SETTINGS_UNIT_LENGTH} = {len})\n"),
338 Some(SourceRange::new(0, 0, literal.module_id)),
340 crate::errors::Tag::Deprecated,
341 ),
342 );
343 }
344 }
345 KclValue::Number { value, meta, ty }
346 }
347 LiteralValue::String(value) => KclValue::String { value, meta },
348 LiteralValue::Bool(value) => KclValue::Bool { value, meta },
349 }
350 }
351
352 pub(crate) fn from_default_param(param: DefaultParamVal, exec_state: &mut ExecState) -> Self {
353 match param {
354 DefaultParamVal::Literal(lit) => Self::from_literal(lit, exec_state),
355 DefaultParamVal::KclNone(value) => KclValue::KclNone {
356 value,
357 meta: Default::default(),
358 },
359 }
360 }
361
362 pub(crate) fn map_env_ref(&self, old_env: usize, new_env: usize) -> Self {
363 let mut result = self.clone();
364 if let KclValue::Function {
365 value: FunctionSource::User { ref mut memory, .. },
366 ..
367 } = result
368 {
369 memory.replace_env(old_env, new_env);
370 }
371 result
372 }
373
374 pub const fn from_number_with_type(f: f64, ty: NumericType, meta: Vec<Metadata>) -> Self {
375 Self::Number { value: f, meta, ty }
376 }
377
378 pub fn from_point2d(p: [f64; 2], ty: NumericType, meta: Vec<Metadata>) -> Self {
380 Self::Tuple {
381 value: vec![
382 Self::Number {
383 value: p[0],
384 meta: meta.clone(),
385 ty: ty.clone(),
386 },
387 Self::Number {
388 value: p[1],
389 meta: meta.clone(),
390 ty,
391 },
392 ],
393 meta,
394 }
395 }
396
397 pub(crate) fn as_usize(&self) -> Option<usize> {
398 match self {
399 KclValue::Number { value, .. } => crate::try_f64_to_usize(*value),
400 _ => None,
401 }
402 }
403
404 pub fn as_int(&self) -> Option<i64> {
405 match self {
406 KclValue::Number { value, .. } => crate::try_f64_to_i64(*value),
407 _ => None,
408 }
409 }
410
411 pub fn as_int_with_ty(&self) -> Option<(i64, NumericType)> {
412 match self {
413 KclValue::Number { value, ty, .. } => crate::try_f64_to_i64(*value).map(|i| (i, ty.clone())),
414 _ => None,
415 }
416 }
417
418 pub fn as_object(&self) -> Option<&KclObjectFields> {
419 if let KclValue::Object { value, meta: _ } = &self {
420 Some(value)
421 } else {
422 None
423 }
424 }
425
426 pub fn into_object(self) -> Option<KclObjectFields> {
427 if let KclValue::Object { value, meta: _ } = self {
428 Some(value)
429 } else {
430 None
431 }
432 }
433
434 pub fn as_str(&self) -> Option<&str> {
435 if let KclValue::String { value, meta: _ } = &self {
436 Some(value)
437 } else {
438 None
439 }
440 }
441
442 pub fn as_array(&self) -> Option<&[KclValue]> {
443 match self {
444 KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } => Some(value),
445 _ => None,
446 }
447 }
448
449 pub fn as_point2d(&self) -> Option<[TyF64; 2]> {
450 let arr = self.as_array()?;
451 if arr.len() != 2 {
452 return None;
453 }
454 let x = arr[0].as_ty_f64()?;
455 let y = arr[1].as_ty_f64()?;
456 Some([x, y])
457 }
458
459 pub fn as_point3d(&self) -> Option<[TyF64; 3]> {
460 let arr = self.as_array()?;
461 if arr.len() != 3 {
462 return None;
463 }
464 let x = arr[0].as_ty_f64()?;
465 let y = arr[1].as_ty_f64()?;
466 let z = arr[2].as_ty_f64()?;
467 Some([x, y, z])
468 }
469
470 pub fn as_uuid(&self) -> Option<uuid::Uuid> {
471 if let KclValue::Uuid { value, meta: _ } = &self {
472 Some(*value)
473 } else {
474 None
475 }
476 }
477
478 pub fn as_plane(&self) -> Option<&Plane> {
479 if let KclValue::Plane { value } = &self {
480 Some(value)
481 } else {
482 None
483 }
484 }
485
486 pub fn as_solid(&self) -> Option<&Solid> {
487 if let KclValue::Solid { value } = &self {
488 Some(value)
489 } else {
490 None
491 }
492 }
493
494 pub fn as_sketch(&self) -> Option<&Sketch> {
495 if let KclValue::Sketch { value } = self {
496 Some(value)
497 } else {
498 None
499 }
500 }
501
502 pub fn as_mut_sketch(&mut self) -> Option<&mut Sketch> {
503 if let KclValue::Sketch { value } = self {
504 Some(value)
505 } else {
506 None
507 }
508 }
509
510 pub fn as_mut_tag(&mut self) -> Option<&mut TagIdentifier> {
511 if let KclValue::TagIdentifier(value) = self {
512 Some(value)
513 } else {
514 None
515 }
516 }
517
518 #[cfg(test)]
519 pub fn as_f64(&self) -> Option<f64> {
520 if let KclValue::Number { value, .. } = &self {
521 Some(*value)
522 } else {
523 None
524 }
525 }
526
527 pub fn as_ty_f64(&self) -> Option<TyF64> {
528 if let KclValue::Number { value, ty, .. } = &self {
529 Some(TyF64::new(*value, ty.clone()))
530 } else {
531 None
532 }
533 }
534
535 pub fn as_bool(&self) -> Option<bool> {
536 if let KclValue::Bool { value, meta: _ } = &self {
537 Some(*value)
538 } else {
539 None
540 }
541 }
542
543 pub fn get_u32(&self, source_ranges: Vec<SourceRange>) -> Result<u32, KclError> {
545 let u = self.as_int().and_then(|n| u64::try_from(n).ok()).ok_or_else(|| {
546 KclError::Semantic(KclErrorDetails::new(
547 "Expected an integer >= 0".to_owned(),
548 source_ranges.clone(),
549 ))
550 })?;
551 u32::try_from(u)
552 .map_err(|_| KclError::Semantic(KclErrorDetails::new("Number was too big".to_owned(), source_ranges)))
553 }
554
555 pub fn get_function(&self) -> Option<&FunctionSource> {
557 match self {
558 KclValue::Function { value, .. } => Some(value),
559 _ => None,
560 }
561 }
562
563 pub fn get_tag_identifier(&self) -> Result<TagIdentifier, KclError> {
565 match self {
566 KclValue::TagIdentifier(t) => Ok(*t.clone()),
567 _ => Err(KclError::Semantic(KclErrorDetails::new(
568 format!("Not a tag identifier: {:?}", self),
569 self.clone().into(),
570 ))),
571 }
572 }
573
574 pub fn get_tag_declarator(&self) -> Result<TagNode, KclError> {
576 match self {
577 KclValue::TagDeclarator(t) => Ok((**t).clone()),
578 _ => Err(KclError::Semantic(KclErrorDetails::new(
579 format!("Not a tag declarator: {:?}", self),
580 self.clone().into(),
581 ))),
582 }
583 }
584
585 pub fn get_bool(&self) -> Result<bool, KclError> {
587 let Self::Bool { value: b, .. } = self else {
588 return Err(KclError::Type(KclErrorDetails::new(
589 format!("Expected bool, found {}", self.human_friendly_type()),
590 self.into(),
591 )));
592 };
593 Ok(*b)
594 }
595
596 pub fn as_fn(&self) -> Option<&FunctionSource> {
597 match self {
598 KclValue::Function { value, .. } => Some(value),
599 _ => None,
600 }
601 }
602
603 pub fn value_str(&self) -> Option<String> {
604 match self {
605 KclValue::Bool { value, .. } => Some(format!("{value}")),
606 KclValue::Number { value, .. } => Some(format!("{value}")),
607 KclValue::String { value, .. } => Some(format!("'{value}'")),
608 KclValue::Uuid { value, .. } => Some(format!("{value}")),
609 KclValue::TagDeclarator(tag) => Some(format!("${}", tag.name)),
610 KclValue::TagIdentifier(tag) => Some(format!("${}", tag.value)),
611 KclValue::Tuple { .. } => Some("[...]".to_owned()),
613 KclValue::HomArray { .. } => Some("[...]".to_owned()),
614 KclValue::Object { .. } => Some("{ ... }".to_owned()),
615 KclValue::Module { .. }
616 | KclValue::Solid { .. }
617 | KclValue::Sketch { .. }
618 | KclValue::Helix { .. }
619 | KclValue::ImportedGeometry(_)
620 | KclValue::Function { .. }
621 | KclValue::Plane { .. }
622 | KclValue::Face { .. }
623 | KclValue::KclNone { .. }
624 | KclValue::Type { .. } => None,
625 }
626 }
627}
628
629impl From<Geometry> for KclValue {
630 fn from(value: Geometry) -> Self {
631 match value {
632 Geometry::Sketch(x) => Self::Sketch { value: Box::new(x) },
633 Geometry::Solid(x) => Self::Solid { value: Box::new(x) },
634 }
635 }
636}
637
638impl From<GeometryWithImportedGeometry> for KclValue {
639 fn from(value: GeometryWithImportedGeometry) -> Self {
640 match value {
641 GeometryWithImportedGeometry::Sketch(x) => Self::Sketch { value: Box::new(x) },
642 GeometryWithImportedGeometry::Solid(x) => Self::Solid { value: Box::new(x) },
643 GeometryWithImportedGeometry::ImportedGeometry(x) => Self::ImportedGeometry(*x),
644 }
645 }
646}