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