1use std::collections::HashMap;
2
3use anyhow::Result;
4use schemars::JsonSchema;
5use serde::{Deserialize, Serialize};
6
7use super::{memory::EnvironmentRef, MetaSettings};
8use crate::{
9 errors::KclErrorDetails,
10 execution::{
11 ExecState, ExecutorContext, Face, Helix, ImportedGeometry, Metadata, Plane, Sketch, SketchSet, Solid, SolidSet,
12 TagIdentifier,
13 },
14 parsing::{
15 ast::types::{
16 DefaultParamVal, FunctionExpression, KclNone, Literal, LiteralValue, Node,
17 PrimitiveType as AstPrimitiveType, TagDeclarator, TagNode, Type,
18 },
19 token::NumericSuffix,
20 },
21 std::{args::Arg, StdFnProps},
22 CompilationError, KclError, ModuleId, SourceRange,
23};
24
25pub type KclObjectFields = HashMap<String, KclValue>;
26
27#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
29#[ts(export)]
30#[serde(tag = "type")]
31pub enum KclValue {
32 Uuid {
33 value: ::uuid::Uuid,
34 #[serde(rename = "__meta")]
35 meta: Vec<Metadata>,
36 },
37 Bool {
38 value: bool,
39 #[serde(rename = "__meta")]
40 meta: Vec<Metadata>,
41 },
42 Number {
43 value: f64,
44 ty: NumericType,
45 #[serde(rename = "__meta")]
46 meta: Vec<Metadata>,
47 },
48 String {
49 value: String,
50 #[serde(rename = "__meta")]
51 meta: Vec<Metadata>,
52 },
53 Array {
54 value: Vec<KclValue>,
55 #[serde(rename = "__meta")]
56 meta: Vec<Metadata>,
57 },
58 Object {
59 value: KclObjectFields,
60 #[serde(rename = "__meta")]
61 meta: Vec<Metadata>,
62 },
63 TagIdentifier(Box<TagIdentifier>),
64 TagDeclarator(crate::parsing::ast::types::BoxNode<TagDeclarator>),
65 Plane {
66 value: Box<Plane>,
67 },
68 Face {
69 value: Box<Face>,
70 },
71 Sketch {
72 value: Box<Sketch>,
73 },
74 Sketches {
75 value: Vec<Box<Sketch>>,
76 },
77 Solid {
78 value: Box<Solid>,
79 },
80 Solids {
81 value: Vec<Box<Solid>>,
82 },
83 Helix {
84 value: Box<Helix>,
85 },
86 ImportedGeometry(ImportedGeometry),
87 #[ts(skip)]
88 Function {
89 #[serde(skip)]
90 value: FunctionSource,
91 #[serde(rename = "__meta")]
92 meta: Vec<Metadata>,
93 },
94 Module {
95 value: ModuleId,
96 #[serde(rename = "__meta")]
97 meta: Vec<Metadata>,
98 },
99 KclNone {
100 value: KclNone,
101 #[serde(rename = "__meta")]
102 meta: Vec<Metadata>,
103 },
104 Tombstone {
106 value: (),
107 #[serde(rename = "__meta")]
108 meta: Vec<Metadata>,
109 },
110}
111
112#[derive(Debug, Clone, PartialEq, Default)]
113pub enum FunctionSource {
114 #[default]
115 None,
116 Std {
117 func: crate::std::StdFn,
118 props: StdFnProps,
119 },
120 User {
121 ast: crate::parsing::ast::types::BoxNode<FunctionExpression>,
122 memory: EnvironmentRef,
123 },
124}
125
126impl JsonSchema for FunctionSource {
127 fn schema_name() -> String {
128 "FunctionSource".to_owned()
129 }
130
131 fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
132 gen.subschema_for::<()>()
134 }
135}
136
137impl From<SketchSet> for KclValue {
138 fn from(sg: SketchSet) -> Self {
139 match sg {
140 SketchSet::Sketch(value) => KclValue::Sketch { value },
141 SketchSet::Sketches(value) => KclValue::Sketches { value },
142 }
143 }
144}
145
146impl From<Vec<Box<Sketch>>> for KclValue {
147 fn from(sg: Vec<Box<Sketch>>) -> Self {
148 KclValue::Sketches { value: sg }
149 }
150}
151
152impl From<SolidSet> for KclValue {
153 fn from(eg: SolidSet) -> Self {
154 match eg {
155 SolidSet::Solid(eg) => KclValue::Solid { value: eg },
156 SolidSet::Solids(egs) => KclValue::Solids { value: egs },
157 }
158 }
159}
160
161impl From<Vec<Box<Solid>>> for KclValue {
162 fn from(eg: Vec<Box<Solid>>) -> Self {
163 if eg.len() == 1 {
164 KclValue::Solid { value: eg[0].clone() }
165 } else {
166 KclValue::Solids { value: eg }
167 }
168 }
169}
170impl From<KclValue> for Vec<SourceRange> {
171 fn from(item: KclValue) -> Self {
172 match item {
173 KclValue::TagDeclarator(t) => vec![SourceRange::new(t.start, t.end, t.module_id)],
174 KclValue::TagIdentifier(t) => to_vec_sr(&t.meta),
175 KclValue::Solid { value } => to_vec_sr(&value.meta),
176 KclValue::Solids { value } => value.iter().flat_map(|eg| to_vec_sr(&eg.meta)).collect(),
177 KclValue::Sketch { value } => to_vec_sr(&value.meta),
178 KclValue::Sketches { value } => value.iter().flat_map(|eg| to_vec_sr(&eg.meta)).collect(),
179 KclValue::Helix { value } => to_vec_sr(&value.meta),
180 KclValue::ImportedGeometry(i) => to_vec_sr(&i.meta),
181 KclValue::Function { meta, .. } => to_vec_sr(&meta),
182 KclValue::Plane { value } => to_vec_sr(&value.meta),
183 KclValue::Face { value } => to_vec_sr(&value.meta),
184 KclValue::Bool { meta, .. } => to_vec_sr(&meta),
185 KclValue::Number { meta, .. } => to_vec_sr(&meta),
186 KclValue::String { meta, .. } => to_vec_sr(&meta),
187 KclValue::Array { meta, .. } => to_vec_sr(&meta),
188 KclValue::Object { meta, .. } => to_vec_sr(&meta),
189 KclValue::Module { meta, .. } => to_vec_sr(&meta),
190 KclValue::Uuid { meta, .. } => to_vec_sr(&meta),
191 KclValue::KclNone { meta, .. } => to_vec_sr(&meta),
192 KclValue::Tombstone { .. } => unreachable!("Tombstone SourceRange"),
193 }
194 }
195}
196
197fn to_vec_sr(meta: &[Metadata]) -> Vec<SourceRange> {
198 meta.iter().map(|m| m.source_range).collect()
199}
200
201impl From<&KclValue> for Vec<SourceRange> {
202 fn from(item: &KclValue) -> Self {
203 match item {
204 KclValue::TagDeclarator(t) => vec![SourceRange::new(t.start, t.end, t.module_id)],
205 KclValue::TagIdentifier(t) => to_vec_sr(&t.meta),
206 KclValue::Solid { value } => to_vec_sr(&value.meta),
207 KclValue::Solids { value } => value.iter().flat_map(|eg| to_vec_sr(&eg.meta)).collect(),
208 KclValue::Sketch { value } => to_vec_sr(&value.meta),
209 KclValue::Sketches { value } => value.iter().flat_map(|eg| to_vec_sr(&eg.meta)).collect(),
210 KclValue::Helix { value } => to_vec_sr(&value.meta),
211 KclValue::ImportedGeometry(i) => to_vec_sr(&i.meta),
212 KclValue::Function { meta, .. } => to_vec_sr(meta),
213 KclValue::Plane { value } => to_vec_sr(&value.meta),
214 KclValue::Face { value } => to_vec_sr(&value.meta),
215 KclValue::Bool { meta, .. } => to_vec_sr(meta),
216 KclValue::Number { meta, .. } => to_vec_sr(meta),
217 KclValue::String { meta, .. } => to_vec_sr(meta),
218 KclValue::Uuid { meta, .. } => to_vec_sr(meta),
219 KclValue::Array { meta, .. } => to_vec_sr(meta),
220 KclValue::Object { meta, .. } => to_vec_sr(meta),
221 KclValue::Module { meta, .. } => to_vec_sr(meta),
222 KclValue::KclNone { meta, .. } => to_vec_sr(meta),
223 KclValue::Tombstone { .. } => unreachable!("Tombstone &SourceRange"),
224 }
225 }
226}
227
228impl KclValue {
229 pub(crate) fn metadata(&self) -> Vec<Metadata> {
230 match self {
231 KclValue::Uuid { value: _, meta } => meta.clone(),
232 KclValue::Bool { value: _, meta } => meta.clone(),
233 KclValue::Number { meta, .. } => meta.clone(),
234 KclValue::String { value: _, meta } => meta.clone(),
235 KclValue::Array { value: _, meta } => meta.clone(),
236 KclValue::Object { value: _, meta } => meta.clone(),
237 KclValue::TagIdentifier(x) => x.meta.clone(),
238 KclValue::TagDeclarator(x) => vec![x.metadata()],
239 KclValue::Plane { value } => value.meta.clone(),
240 KclValue::Face { value } => value.meta.clone(),
241 KclValue::Sketch { value } => value.meta.clone(),
242 KclValue::Sketches { value } => value.iter().flat_map(|sketch| &sketch.meta).copied().collect(),
243 KclValue::Solid { value } => value.meta.clone(),
244 KclValue::Solids { value } => value.iter().flat_map(|sketch| &sketch.meta).copied().collect(),
245 KclValue::Helix { value } => value.meta.clone(),
246 KclValue::ImportedGeometry(x) => x.meta.clone(),
247 KclValue::Function { meta, .. } => meta.clone(),
248 KclValue::Module { meta, .. } => meta.clone(),
249 KclValue::KclNone { meta, .. } => meta.clone(),
250 KclValue::Tombstone { .. } => unreachable!("Tombstone Metadata"),
251 }
252 }
253
254 pub(crate) fn function_def_source_range(&self) -> Option<SourceRange> {
255 let KclValue::Function {
256 value: FunctionSource::User { ast, .. },
257 ..
258 } = self
259 else {
260 return None;
261 };
262 Some(ast.as_source_range())
265 }
266
267 pub(crate) fn get_solid_set(&self) -> Result<SolidSet> {
268 match self {
269 KclValue::Solid { value } => Ok(SolidSet::Solid(value.clone())),
270 KclValue::Solids { value } => Ok(SolidSet::Solids(value.clone())),
271 KclValue::Array { value, .. } => {
272 let solids: Vec<_> = value
273 .iter()
274 .enumerate()
275 .map(|(i, v)| {
276 v.as_solid().map(|v| v.to_owned()).map(Box::new).ok_or_else(|| {
277 anyhow::anyhow!(
278 "expected this array to only contain solids, but element {i} was actually {}",
279 v.human_friendly_type()
280 )
281 })
282 })
283 .collect::<Result<_, _>>()?;
284 Ok(SolidSet::Solids(solids))
285 }
286 _ => anyhow::bail!("Not a solid or solids: {:?}", self),
287 }
288 }
289
290 #[allow(unused)]
291 pub(crate) fn none() -> Self {
292 Self::KclNone {
293 value: Default::default(),
294 meta: Default::default(),
295 }
296 }
297
298 pub(crate) fn human_friendly_type(&self) -> &'static str {
301 match self {
302 KclValue::Uuid { .. } => "Unique ID (uuid)",
303 KclValue::TagDeclarator(_) => "TagDeclarator",
304 KclValue::TagIdentifier(_) => "TagIdentifier",
305 KclValue::Solid { .. } => "Solid",
306 KclValue::Solids { .. } => "Solids",
307 KclValue::Sketch { .. } => "Sketch",
308 KclValue::Sketches { .. } => "Sketches",
309 KclValue::Helix { .. } => "Helix",
310 KclValue::ImportedGeometry(_) => "ImportedGeometry",
311 KclValue::Function { .. } => "Function",
312 KclValue::Plane { .. } => "Plane",
313 KclValue::Face { .. } => "Face",
314 KclValue::Bool { .. } => "boolean (true/false value)",
315 KclValue::Number { .. } => "number",
316 KclValue::String { .. } => "string (text)",
317 KclValue::Array { .. } => "array (list)",
318 KclValue::Object { .. } => "object",
319 KclValue::Module { .. } => "module",
320 KclValue::KclNone { .. } => "None",
321 KclValue::Tombstone { .. } => "TOMBSTONE",
322 }
323 }
324
325 pub(crate) fn from_literal(literal: Node<Literal>, settings: &MetaSettings) -> Self {
326 let meta = vec![literal.metadata()];
327 match literal.inner.value {
328 LiteralValue::Number { value, suffix } => KclValue::Number {
329 value,
330 meta,
331 ty: NumericType::from_parsed(suffix, settings),
332 },
333 LiteralValue::String(value) => KclValue::String { value, meta },
334 LiteralValue::Bool(value) => KclValue::Bool { value, meta },
335 }
336 }
337
338 pub(crate) fn from_default_param(param: DefaultParamVal, settings: &MetaSettings) -> Self {
339 match param {
340 DefaultParamVal::Literal(lit) => Self::from_literal(lit, settings),
341 DefaultParamVal::KclNone(none) => KclValue::KclNone {
342 value: none,
343 meta: Default::default(),
344 },
345 }
346 }
347
348 pub(crate) fn map_env_ref(&self, env_map: &HashMap<EnvironmentRef, EnvironmentRef>) -> Self {
349 let mut result = self.clone();
350 if let KclValue::Function {
351 value: FunctionSource::User { ref mut memory, .. },
352 ..
353 } = result
354 {
355 if let Some(new) = env_map.get(memory) {
356 *memory = *new;
357 }
358 }
359 result
360 }
361
362 pub const fn from_number(f: f64, meta: Vec<Metadata>) -> Self {
364 Self::Number {
365 value: f,
366 meta,
367 ty: NumericType::Unknown,
368 }
369 }
370
371 pub const fn from_number_with_type(f: f64, ty: NumericType, meta: Vec<Metadata>) -> Self {
372 Self::Number { value: f, meta, ty }
373 }
374
375 pub fn from_point2d(p: [f64; 2], ty: NumericType, meta: Vec<Metadata>) -> Self {
377 Self::Array {
378 value: vec![
379 Self::Number {
380 value: p[0],
381 meta: meta.clone(),
382 ty: ty.clone(),
383 },
384 Self::Number {
385 value: p[1],
386 meta: meta.clone(),
387 ty,
388 },
389 ],
390 meta,
391 }
392 }
393
394 pub(crate) fn as_usize(&self) -> Option<usize> {
395 match self {
396 KclValue::Number { value, .. } => crate::try_f64_to_usize(*value),
397 _ => None,
398 }
399 }
400
401 pub fn as_int(&self) -> Option<i64> {
402 match self {
403 KclValue::Number { value, .. } => crate::try_f64_to_i64(*value),
404 _ => None,
405 }
406 }
407
408 pub fn as_object(&self) -> Option<&KclObjectFields> {
409 if let KclValue::Object { value, meta: _ } = &self {
410 Some(value)
411 } else {
412 None
413 }
414 }
415
416 pub fn into_object(self) -> Option<KclObjectFields> {
417 if let KclValue::Object { value, meta: _ } = self {
418 Some(value)
419 } else {
420 None
421 }
422 }
423
424 pub fn as_str(&self) -> Option<&str> {
425 if let KclValue::String { value, meta: _ } = &self {
426 Some(value)
427 } else {
428 None
429 }
430 }
431
432 pub fn as_array(&self) -> Option<&[KclValue]> {
433 if let KclValue::Array { value, meta: _ } = &self {
434 Some(value)
435 } else {
436 None
437 }
438 }
439
440 pub fn as_point2d(&self) -> Option<[f64; 2]> {
441 let arr = self.as_array()?;
442 if arr.len() != 2 {
443 return None;
444 }
445 let x = arr[0].as_f64()?;
446 let y = arr[1].as_f64()?;
447 Some([x, y])
448 }
449
450 pub fn as_uuid(&self) -> Option<uuid::Uuid> {
451 if let KclValue::Uuid { value, meta: _ } = &self {
452 Some(*value)
453 } else {
454 None
455 }
456 }
457
458 pub fn as_plane(&self) -> Option<&Plane> {
459 if let KclValue::Plane { value } = &self {
460 Some(value)
461 } else {
462 None
463 }
464 }
465
466 pub fn as_solid(&self) -> Option<&Solid> {
467 if let KclValue::Solid { value } = &self {
468 Some(value)
469 } else {
470 None
471 }
472 }
473
474 pub fn as_sketch(&self) -> Option<&Sketch> {
475 if let KclValue::Sketch { value } = self {
476 Some(value)
477 } else {
478 None
479 }
480 }
481
482 pub fn as_f64(&self) -> Option<f64> {
483 if let KclValue::Number { value, .. } = &self {
484 Some(*value)
485 } else {
486 None
487 }
488 }
489
490 pub fn as_bool(&self) -> Option<bool> {
491 if let KclValue::Bool { value, meta: _ } = &self {
492 Some(*value)
493 } else {
494 None
495 }
496 }
497
498 pub fn get_u32(&self, source_ranges: Vec<SourceRange>) -> Result<u32, KclError> {
500 let u = self.as_int().and_then(|n| u64::try_from(n).ok()).ok_or_else(|| {
501 KclError::Semantic(KclErrorDetails {
502 message: "Expected an integer >= 0".to_owned(),
503 source_ranges: source_ranges.clone(),
504 })
505 })?;
506 u32::try_from(u).map_err(|_| {
507 KclError::Semantic(KclErrorDetails {
508 message: "Number was too big".to_owned(),
509 source_ranges,
510 })
511 })
512 }
513
514 pub fn get_function(&self) -> Option<&FunctionSource> {
516 match self {
517 KclValue::Function { value, .. } => Some(value),
518 _ => None,
519 }
520 }
521
522 pub fn get_tag_identifier(&self) -> Result<TagIdentifier, KclError> {
524 match self {
525 KclValue::TagIdentifier(t) => Ok(*t.clone()),
526 _ => Err(KclError::Semantic(KclErrorDetails {
527 message: format!("Not a tag identifier: {:?}", self),
528 source_ranges: self.clone().into(),
529 })),
530 }
531 }
532
533 pub fn get_tag_declarator(&self) -> Result<TagNode, KclError> {
535 match self {
536 KclValue::TagDeclarator(t) => Ok((**t).clone()),
537 _ => Err(KclError::Semantic(KclErrorDetails {
538 message: format!("Not a tag declarator: {:?}", self),
539 source_ranges: self.clone().into(),
540 })),
541 }
542 }
543
544 pub fn get_tag_declarator_opt(&self) -> Result<Option<TagNode>, KclError> {
546 match self {
547 KclValue::TagDeclarator(t) => Ok(Some((**t).clone())),
548 _ => Err(KclError::Semantic(KclErrorDetails {
549 message: format!("Not a tag declarator: {:?}", self),
550 source_ranges: self.clone().into(),
551 })),
552 }
553 }
554
555 pub fn get_bool(&self) -> Result<bool, KclError> {
557 let Self::Bool { value: b, .. } = self else {
558 return Err(KclError::Type(KclErrorDetails {
559 source_ranges: self.into(),
560 message: format!("Expected bool, found {}", self.human_friendly_type()),
561 }));
562 };
563 Ok(*b)
564 }
565
566 pub fn has_type(&self, ty: &RuntimeType) -> bool {
568 let Some(self_ty) = self.ty() else {
569 return false;
570 };
571
572 self_ty.subtype(ty)
573 }
574
575 fn ty(&self) -> Option<RuntimeType> {
576 match self {
577 KclValue::Bool { .. } => Some(RuntimeType::Primitive(PrimitiveType::Boolean)),
578 KclValue::Number { ty, .. } => Some(RuntimeType::Primitive(PrimitiveType::Number(ty.clone()))),
579 KclValue::String { .. } => Some(RuntimeType::Primitive(PrimitiveType::String)),
580 KclValue::Object { value, .. } => {
581 let properties = value
582 .iter()
583 .map(|(k, v)| v.ty().map(|t| (k.clone(), t)))
584 .collect::<Option<Vec<_>>>()?;
585 Some(RuntimeType::Object(properties))
586 }
587 KclValue::Plane { .. } => Some(RuntimeType::Primitive(PrimitiveType::Plane)),
588 KclValue::Sketch { .. } => Some(RuntimeType::Primitive(PrimitiveType::Sketch)),
589 KclValue::Sketches { .. } => Some(RuntimeType::Array(PrimitiveType::Sketch)),
590 KclValue::Solid { .. } => Some(RuntimeType::Primitive(PrimitiveType::Solid)),
591 KclValue::Solids { .. } => Some(RuntimeType::Array(PrimitiveType::Solid)),
592 KclValue::Array { value, .. } => Some(RuntimeType::Tuple(
593 value
594 .iter()
595 .map(|v| v.ty().and_then(RuntimeType::primitive))
596 .collect::<Option<Vec<_>>>()?,
597 )),
598 KclValue::Face { .. } => None,
599 KclValue::Helix { .. } => None,
600 KclValue::ImportedGeometry(..) => None,
601 KclValue::Function { .. } => None,
602 KclValue::Module { .. } => None,
603 KclValue::TagIdentifier(_) => None,
604 KclValue::TagDeclarator(_) => None,
605 KclValue::KclNone { .. } => None,
606 KclValue::Uuid { .. } => None,
607 KclValue::Tombstone { .. } => None,
608 }
609 }
610
611 pub async fn call_fn(
614 &self,
615 args: Vec<Arg>,
616 exec_state: &mut ExecState,
617 ctx: ExecutorContext,
618 source_range: SourceRange,
619 ) -> Result<Option<KclValue>, KclError> {
620 match self {
621 KclValue::Function {
622 value: FunctionSource::Std { func, props },
623 ..
624 } => {
625 if props.deprecated {
626 exec_state.warn(CompilationError::err(
627 source_range,
628 format!(
629 "`{}` is deprecated, see the docs for a recommended replacement",
630 props.name
631 ),
632 ));
633 }
634 exec_state.mut_memory().push_new_env_for_rust_call();
635 let args = crate::std::Args::new(
636 args,
637 source_range,
638 ctx.clone(),
639 exec_state.mod_local.pipe_value.clone().map(Arg::synthetic),
640 );
641 let result = func(exec_state, args).await.map(Some);
642 exec_state.mut_memory().pop_env();
643 result
644 }
645 KclValue::Function {
646 value: FunctionSource::User { ast, memory },
647 ..
648 } => crate::execution::exec_ast::call_user_defined_function(args, *memory, ast, exec_state, &ctx).await,
649 _ => Err(KclError::Semantic(KclErrorDetails {
650 message: "cannot call this because it isn't a function".to_string(),
651 source_ranges: vec![source_range],
652 })),
653 }
654 }
655
656 pub async fn call_fn_kw(
659 &self,
660 args: crate::std::Args,
661 exec_state: &mut ExecState,
662 ctx: ExecutorContext,
663 callsite: SourceRange,
664 ) -> Result<Option<KclValue>, KclError> {
665 match self {
666 KclValue::Function {
667 value: FunctionSource::Std { func: _, props },
668 ..
669 } => {
670 if props.deprecated {
671 exec_state.warn(CompilationError::err(
672 callsite,
673 format!(
674 "`{}` is deprecated, see the docs for a recommended replacement",
675 props.name
676 ),
677 ));
678 }
679 todo!("Implement KCL stdlib fns with keyword args");
680 }
681 KclValue::Function {
682 value: FunctionSource::User { ast, memory },
683 ..
684 } => {
685 crate::execution::exec_ast::call_user_defined_function_kw(args.kw_args, *memory, ast, exec_state, &ctx)
686 .await
687 }
688 _ => Err(KclError::Semantic(KclErrorDetails {
689 message: "cannot call this because it isn't a function".to_string(),
690 source_ranges: vec![callsite],
691 })),
692 }
693 }
694}
695
696#[derive(Debug, Clone, PartialEq)]
697pub enum RuntimeType {
698 Primitive(PrimitiveType),
699 Array(PrimitiveType),
700 Tuple(Vec<PrimitiveType>),
701 Object(Vec<(String, RuntimeType)>),
702}
703
704impl RuntimeType {
705 pub fn from_parsed(value: Type, settings: &super::MetaSettings) -> Option<Self> {
706 match value {
707 Type::Primitive(pt) => Some(RuntimeType::Primitive(PrimitiveType::from_parsed(pt, settings)?)),
708 Type::Array(pt) => Some(RuntimeType::Array(PrimitiveType::from_parsed(pt, settings)?)),
709 Type::Object { properties } => Some(RuntimeType::Object(
710 properties
711 .into_iter()
712 .map(|p| {
713 p.type_.and_then(|t| {
714 RuntimeType::from_parsed(t.inner, settings).map(|ty| (p.identifier.inner.name, ty))
715 })
716 })
717 .collect::<Option<Vec<_>>>()?,
718 )),
719 }
720 }
721
722 fn subtype(&self, sup: &RuntimeType) -> bool {
724 use RuntimeType::*;
725
726 match (self, sup) {
727 (Primitive(t1), Primitive(t2)) | (Array(t1), Array(t2)) => t1 == t2,
729 (Tuple(t1), Tuple(t2)) => t1 == t2,
730 (Tuple(t1), Array(t2)) => t1.iter().all(|t| t == t2),
731 (Object(t1), Object(t2)) => t1 == t2,
733 _ => false,
734 }
735 }
736
737 fn primitive(self) -> Option<PrimitiveType> {
738 match self {
739 RuntimeType::Primitive(t) => Some(t),
740 _ => None,
741 }
742 }
743}
744
745#[derive(Debug, Clone, PartialEq)]
746pub enum PrimitiveType {
747 Number(NumericType),
748 String,
749 Boolean,
750 Sketch,
751 Solid,
752 Plane,
753}
754
755impl PrimitiveType {
756 fn from_parsed(value: AstPrimitiveType, settings: &super::MetaSettings) -> Option<Self> {
757 match value {
758 AstPrimitiveType::String => Some(PrimitiveType::String),
759 AstPrimitiveType::Boolean => Some(PrimitiveType::Boolean),
760 AstPrimitiveType::Number(suffix) => Some(PrimitiveType::Number(NumericType::from_parsed(suffix, settings))),
761 AstPrimitiveType::Sketch => Some(PrimitiveType::Sketch),
762 AstPrimitiveType::Solid => Some(PrimitiveType::Solid),
763 AstPrimitiveType::Plane => Some(PrimitiveType::Plane),
764 _ => None,
765 }
766 }
767}
768
769#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
770#[ts(export)]
771#[serde(tag = "type")]
772pub enum NumericType {
773 Known(UnitType),
775 Default { len: UnitLen, angle: UnitAngle },
777 Unknown,
779 Any,
781}
782
783impl NumericType {
784 pub fn count() -> Self {
785 NumericType::Known(UnitType::Count)
786 }
787
788 pub fn combine_eq(self, other: &NumericType) -> NumericType {
790 if &self == other {
791 self
792 } else {
793 NumericType::Unknown
794 }
795 }
796
797 pub fn combine_n_eq(tys: &[NumericType]) -> NumericType {
801 let ty0 = tys[0].clone();
802 for t in &tys[1..] {
803 if t != &ty0 {
804 return NumericType::Unknown;
805 }
806 }
807 ty0
808 }
809
810 pub fn combine_add(a: NumericType, b: NumericType) -> NumericType {
812 if a == b {
813 return a;
814 }
815 NumericType::Unknown
816 }
817
818 pub fn combine_mul(a: NumericType, b: NumericType) -> NumericType {
820 if a == NumericType::count() {
821 return b;
822 }
823 if b == NumericType::count() {
824 return a;
825 }
826 NumericType::Unknown
827 }
828
829 pub fn combine_div(a: NumericType, b: NumericType) -> NumericType {
831 if b == NumericType::count() {
832 return a;
833 }
834 NumericType::Unknown
835 }
836
837 pub fn from_parsed(suffix: NumericSuffix, settings: &super::MetaSettings) -> Self {
838 match suffix {
839 NumericSuffix::None => NumericType::Default {
840 len: settings.default_length_units,
841 angle: settings.default_angle_units,
842 },
843 NumericSuffix::Count => NumericType::Known(UnitType::Count),
844 NumericSuffix::Mm => NumericType::Known(UnitType::Length(UnitLen::Mm)),
845 NumericSuffix::Cm => NumericType::Known(UnitType::Length(UnitLen::Cm)),
846 NumericSuffix::M => NumericType::Known(UnitType::Length(UnitLen::M)),
847 NumericSuffix::Inch => NumericType::Known(UnitType::Length(UnitLen::Inches)),
848 NumericSuffix::Ft => NumericType::Known(UnitType::Length(UnitLen::Feet)),
849 NumericSuffix::Yd => NumericType::Known(UnitType::Length(UnitLen::Yards)),
850 NumericSuffix::Deg => NumericType::Known(UnitType::Angle(UnitAngle::Degrees)),
851 NumericSuffix::Rad => NumericType::Known(UnitType::Angle(UnitAngle::Radians)),
852 }
853 }
854}
855
856impl From<UnitLen> for NumericType {
857 fn from(value: UnitLen) -> Self {
858 NumericType::Known(UnitType::Length(value))
859 }
860}
861
862impl From<UnitAngle> for NumericType {
863 fn from(value: UnitAngle) -> Self {
864 NumericType::Known(UnitType::Angle(value))
865 }
866}
867
868#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, ts_rs::TS, JsonSchema)]
869#[ts(export)]
870#[serde(tag = "type")]
871pub enum UnitType {
872 Count,
873 Length(UnitLen),
874 Angle(UnitAngle),
875}
876
877#[derive(Debug, Default, Clone, Copy, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Eq)]
880#[ts(export)]
881#[serde(tag = "type")]
882pub enum UnitLen {
883 #[default]
884 Mm,
885 Cm,
886 M,
887 Inches,
888 Feet,
889 Yards,
890}
891
892impl std::fmt::Display for UnitLen {
893 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
894 match self {
895 UnitLen::Mm => write!(f, "mm"),
896 UnitLen::Cm => write!(f, "cm"),
897 UnitLen::M => write!(f, "m"),
898 UnitLen::Inches => write!(f, "in"),
899 UnitLen::Feet => write!(f, "ft"),
900 UnitLen::Yards => write!(f, "yd"),
901 }
902 }
903}
904
905impl TryFrom<NumericSuffix> for UnitLen {
906 type Error = ();
907
908 fn try_from(suffix: NumericSuffix) -> std::result::Result<Self, Self::Error> {
909 match suffix {
910 NumericSuffix::Mm => Ok(Self::Mm),
911 NumericSuffix::Cm => Ok(Self::Cm),
912 NumericSuffix::M => Ok(Self::M),
913 NumericSuffix::Inch => Ok(Self::Inches),
914 NumericSuffix::Ft => Ok(Self::Feet),
915 NumericSuffix::Yd => Ok(Self::Yards),
916 _ => Err(()),
917 }
918 }
919}
920
921impl From<crate::UnitLength> for UnitLen {
922 fn from(unit: crate::UnitLength) -> Self {
923 match unit {
924 crate::UnitLength::Cm => UnitLen::Cm,
925 crate::UnitLength::Ft => UnitLen::Feet,
926 crate::UnitLength::In => UnitLen::Inches,
927 crate::UnitLength::M => UnitLen::M,
928 crate::UnitLength::Mm => UnitLen::Mm,
929 crate::UnitLength::Yd => UnitLen::Yards,
930 }
931 }
932}
933
934impl From<UnitLen> for crate::UnitLength {
935 fn from(unit: UnitLen) -> Self {
936 match unit {
937 UnitLen::Cm => crate::UnitLength::Cm,
938 UnitLen::Feet => crate::UnitLength::Ft,
939 UnitLen::Inches => crate::UnitLength::In,
940 UnitLen::M => crate::UnitLength::M,
941 UnitLen::Mm => crate::UnitLength::Mm,
942 UnitLen::Yards => crate::UnitLength::Yd,
943 }
944 }
945}
946
947impl From<UnitLen> for kittycad_modeling_cmds::units::UnitLength {
948 fn from(unit: UnitLen) -> Self {
949 match unit {
950 UnitLen::Cm => kittycad_modeling_cmds::units::UnitLength::Centimeters,
951 UnitLen::Feet => kittycad_modeling_cmds::units::UnitLength::Feet,
952 UnitLen::Inches => kittycad_modeling_cmds::units::UnitLength::Inches,
953 UnitLen::M => kittycad_modeling_cmds::units::UnitLength::Meters,
954 UnitLen::Mm => kittycad_modeling_cmds::units::UnitLength::Millimeters,
955 UnitLen::Yards => kittycad_modeling_cmds::units::UnitLength::Yards,
956 }
957 }
958}
959
960#[derive(Debug, Default, Clone, Copy, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Eq)]
962#[ts(export)]
963#[serde(tag = "type")]
964pub enum UnitAngle {
965 #[default]
966 Degrees,
967 Radians,
968}
969
970impl std::fmt::Display for UnitAngle {
971 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
972 match self {
973 UnitAngle::Degrees => write!(f, "deg"),
974 UnitAngle::Radians => write!(f, "rad"),
975 }
976 }
977}
978
979impl TryFrom<NumericSuffix> for UnitAngle {
980 type Error = ();
981
982 fn try_from(suffix: NumericSuffix) -> std::result::Result<Self, Self::Error> {
983 match suffix {
984 NumericSuffix::Deg => Ok(Self::Degrees),
985 NumericSuffix::Rad => Ok(Self::Radians),
986 _ => Err(()),
987 }
988 }
989}