Skip to main content

kcl_api/
front.rs

1use kcl_error::SourceRange;
2use schemars::JsonSchema;
3use serde::Deserialize;
4use serde::Serialize;
5
6use crate::ArtifactId;
7use crate::NodePath;
8use crate::NumericSuffix;
9use crate::UnitLength;
10use crate::artifact::PlaneName;
11
12mod sketch;
13pub use sketch::*;
14
15#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, PartialOrd, Ord, Deserialize, Serialize, JsonSchema, ts_rs::TS)]
16#[ts(export, export_to = "FrontendApi.ts", rename = "ApiObjectId")]
17pub struct ObjectId(pub usize);
18
19impl ObjectId {
20    pub fn predecessor(self) -> Option<Self> {
21        self.0.checked_sub(1).map(ObjectId)
22    }
23}
24
25#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
26#[ts(export, export_to = "FrontendApi.ts", rename = "ApiObject")]
27pub struct Object {
28    pub id: ObjectId,
29    pub kind: ObjectKind,
30    pub label: String,
31    pub comments: String,
32    pub artifact_id: ArtifactId,
33    pub source: SourceRef,
34}
35
36impl Object {
37    pub fn placeholder(id: ObjectId, range: SourceRange, node_path: Option<NodePath>) -> Self {
38        Object {
39            id,
40            kind: ObjectKind::Nil,
41            label: Default::default(),
42            comments: Default::default(),
43            artifact_id: ArtifactId::placeholder(),
44            source: SourceRef::new(range, node_path),
45        }
46    }
47}
48
49#[allow(clippy::large_enum_variant)]
50#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
51#[ts(export, export_to = "FrontendApi.ts", rename = "ApiObjectKind")]
52#[serde(tag = "type")]
53pub enum ObjectKind {
54    /// A placeholder for an object that will be solved and replaced later.
55    Nil,
56    Plane(Plane),
57    Face(Face),
58    Wall(Wall),
59    Cap(Cap),
60    Sketch(crate::front::sketch::Sketch),
61    // These need to be named since the nested types are also enums. ts-rs needs
62    // a place to put the type tag.
63    Segment {
64        segment: sketch::Segment,
65    },
66    Constraint {
67        constraint: sketch::Constraint,
68    },
69}
70
71#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
72#[ts(export, export_to = "FrontendApi.ts", rename = "ApiCap")]
73#[serde(rename_all = "camelCase")]
74pub struct Cap {
75    pub id: ObjectId,
76    pub kind: CapKind,
77    #[serde(skip_deserializing)]
78    pub source: CapSource,
79    #[serde(default, skip_serializing_if = "Option::is_none")]
80    #[ts(optional)]
81    pub solid_output_index: Option<usize>,
82}
83
84#[derive(Debug, Clone, Default, PartialEq, Serialize, ts_rs::TS)]
85#[ts(export, export_to = "FrontendApi.ts", rename = "ApiCapSource")]
86#[serde(rename_all = "camelCase")]
87pub struct CapSource {
88    pub solid: SourceRefRange,
89    pub sweep: SourceRefRange,
90}
91
92#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, ts_rs::TS)]
93#[ts(export, export_to = "FrontendApi.ts", rename = "ApiCapKind")]
94#[serde(rename_all = "camelCase")]
95pub enum CapKind {
96    Start,
97    End,
98}
99
100#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
101#[ts(export, export_to = "FrontendApi.ts", rename = "ApiSourceRef")]
102#[serde(tag = "type")]
103pub enum SourceRef {
104    Simple {
105        range: SourceRange,
106        node_path: Option<NodePath>,
107    },
108    BackTrace {
109        ranges: Vec<(SourceRange, Option<NodePath>)>,
110    },
111}
112
113impl SourceRef {
114    pub fn new(range: SourceRange, node_path: Option<NodePath>) -> Self {
115        Self::Simple { range, node_path }
116    }
117}
118
119#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
120#[ts(export, export_to = "FrontendApi.ts", rename = "ApiPlane")]
121#[serde(rename_all = "camelCase")]
122pub enum Plane {
123    Object(ObjectId),
124    Default(PlaneName),
125    PrimitiveFace(PrimitiveFacePlane),
126}
127
128/// An indexed face on a solid which has not yet been materialized as a KCL
129/// `Face` value.
130#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
131#[ts(export, export_to = "FrontendApi.ts")]
132#[serde(rename_all = "camelCase")]
133pub struct PrimitiveFacePlane {
134    /// Engine ID of the solid which owns the face.
135    pub solid_id: uuid::Uuid,
136    pub index: usize,
137}
138
139#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
140#[ts(export, export_to = "FrontendApi.ts", rename = "ApiFace")]
141#[serde(rename_all = "camelCase")]
142pub struct Face {
143    pub id: ObjectId,
144}
145
146#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
147#[ts(export, export_to = "FrontendApi.ts", rename = "ApiWall")]
148#[serde(rename_all = "camelCase")]
149pub struct Wall {
150    pub id: ObjectId,
151    #[serde(skip_deserializing)]
152    pub source: WallSource,
153    #[serde(default, skip_serializing_if = "Option::is_none")]
154    #[ts(optional)]
155    pub solid_output_index: Option<usize>,
156}
157
158#[derive(Debug, Clone, Default, PartialEq, Serialize, ts_rs::TS)]
159#[ts(export, export_to = "FrontendApi.ts", rename = "ApiWallSource")]
160#[serde(rename_all = "camelCase")]
161pub struct WallSource {
162    pub solid: SourceRefRange,
163    pub sweep: SourceRefRange,
164    #[serde(default, skip_serializing_if = "Option::is_none")]
165    #[ts(optional)]
166    pub path: Option<SourceRefRange>,
167    pub segment: SourceRefRange,
168}
169
170#[derive(Debug, Clone, Default, PartialEq, Serialize, ts_rs::TS)]
171#[ts(export, export_to = "FrontendApi.ts", rename = "ApiSourceRefRange")]
172#[serde(rename_all = "camelCase")]
173pub struct SourceRefRange {
174    pub range: SourceRange,
175    pub node_path: Option<NodePath>,
176}
177
178#[derive(Debug, Clone, Copy, PartialEq, Deserialize, Serialize, ts_rs::TS)]
179#[ts(export, export_to = "FrontendApi.ts")]
180pub struct Number {
181    pub value: f64,
182    pub units: NumericSuffix,
183}
184
185#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
186#[ts(export, export_to = "FrontendApi.ts")]
187#[serde(tag = "type")]
188pub enum Expr {
189    Number(Number),
190    Var(Number),
191    Variable(String),
192}
193
194impl From<SourceRange> for SourceRef {
195    fn from(value: SourceRange) -> Self {
196        Self::Simple {
197            range: value,
198            node_path: None,
199        }
200    }
201}
202
203impl From<(f64, UnitLength)> for Number {
204    fn from((value, units): (f64, UnitLength)) -> Self {
205        // Direct conversion from UnitLength to NumericSuffix (never panics)
206        let units_suffix = NumericSuffix::from(units);
207        Number {
208            value,
209            units: units_suffix,
210        }
211    }
212}