1#![allow(async_fn_in_trait)]
4
5use kcl_error::SourceRange;
6use serde::{Deserialize, Serialize};
7
8pub use crate::ExecutorSettings as Settings;
9use crate::{ExecOutcome, engine::PlaneName, execution::ArtifactId, pretty::NumericSuffix};
10
11pub trait LifecycleApi {
12 async fn open_project(&self, project: ProjectId, files: Vec<File>, open_file: FileId) -> Result<()>;
13 async fn add_file(&self, project: ProjectId, file: File) -> Result<()>;
14 async fn remove_file(&self, project: ProjectId, file: FileId) -> Result<()>;
15 async fn update_file(&self, project: ProjectId, file: FileId, text: String) -> Result<()>;
17 async fn switch_file(&self, project: ProjectId, file: FileId) -> Result<()>;
18 async fn refresh(&self, project: ProjectId) -> Result<()>;
19}
20
21#[derive(Debug, Clone, Deserialize, Serialize, ts_rs::TS)]
22#[ts(export, export_to = "FrontendApi.ts")]
23pub struct SceneGraph {
24 pub project: ProjectId,
25 pub file: FileId,
26 pub version: Version,
27
28 pub objects: Vec<Object>,
29 pub settings: Settings,
30 pub sketch_mode: Option<ObjectId>,
31}
32
33impl SceneGraph {
34 pub fn empty(project: ProjectId, file: FileId, version: Version) -> Self {
35 SceneGraph {
36 project,
37 file,
38 version,
39 objects: Vec::new(),
40 settings: Default::default(),
41 sketch_mode: None,
42 }
43 }
44}
45
46#[derive(Debug, Clone, Serialize, ts_rs::TS)]
47#[ts(export, export_to = "FrontendApi.ts")]
48pub struct SceneGraphDelta {
49 pub new_graph: SceneGraph,
50 pub new_objects: Vec<ObjectId>,
51 pub invalidates_ids: bool,
52 pub exec_outcome: ExecOutcome,
53}
54
55impl SceneGraphDelta {
56 pub fn new(
57 new_graph: SceneGraph,
58 new_objects: Vec<ObjectId>,
59 invalidates_ids: bool,
60 exec_outcome: ExecOutcome,
61 ) -> Self {
62 SceneGraphDelta {
63 new_graph,
64 new_objects,
65 invalidates_ids,
66 exec_outcome,
67 }
68 }
69}
70
71#[derive(Debug, Clone, Deserialize, Serialize, ts_rs::TS)]
72#[ts(export, export_to = "FrontendApi.ts")]
73pub struct SourceDelta {
74 pub text: String,
75}
76
77#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Deserialize, Serialize, ts_rs::TS)]
78#[ts(export, export_to = "FrontendApi.ts", rename = "ApiObjectId")]
79pub struct ObjectId(pub usize);
80
81impl ObjectId {
82 pub fn predecessor(self) -> Option<Self> {
83 self.0.checked_sub(1).map(ObjectId)
84 }
85}
86
87#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd, Deserialize, Serialize, ts_rs::TS)]
88#[ts(export, export_to = "FrontendApi.ts", rename = "ApiVersion")]
89pub struct Version(pub usize);
90
91#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Deserialize, Serialize, ts_rs::TS)]
92#[ts(export, export_to = "FrontendApi.ts", rename = "ApiProjectId")]
93pub struct ProjectId(pub usize);
94
95#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Deserialize, Serialize, ts_rs::TS)]
96#[ts(export, export_to = "FrontendApi.ts", rename = "ApiFileId")]
97pub struct FileId(pub usize);
98
99#[derive(Debug, Clone, Deserialize, Serialize, ts_rs::TS)]
100#[ts(export, export_to = "FrontendApi.ts", rename = "ApiFile")]
101pub struct File {
102 pub id: FileId,
103 pub path: String,
104 pub text: String,
105}
106
107#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
108#[ts(export, export_to = "FrontendApi.ts", rename = "ApiObject")]
109pub struct Object {
110 pub id: ObjectId,
111 pub kind: ObjectKind,
112 pub label: String,
113 pub comments: String,
114 pub artifact_id: ArtifactId,
115 pub source: SourceRef,
116}
117
118impl Object {
119 pub fn placeholder(id: ObjectId, range: SourceRange) -> Self {
120 Object {
121 id,
122 kind: ObjectKind::Nil,
123 label: Default::default(),
124 comments: Default::default(),
125 artifact_id: ArtifactId::placeholder(),
126 source: SourceRef::Simple { range },
127 }
128 }
129}
130
131#[allow(clippy::large_enum_variant)]
132#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
133#[ts(export, export_to = "FrontendApi.ts", rename = "ApiObjectKind")]
134#[serde(tag = "type")]
135pub enum ObjectKind {
136 Nil,
138 Plane(Plane),
139 Face(Face),
140 Sketch(crate::frontend::sketch::Sketch),
141 Segment {
144 segment: crate::frontend::sketch::Segment,
145 },
146 Constraint {
147 constraint: crate::frontend::sketch::Constraint,
148 },
149}
150
151#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
152#[ts(export, export_to = "FrontendApi.ts", rename = "ApiPlane")]
153#[serde(rename_all = "camelCase")]
154pub enum Plane {
155 Object(ObjectId),
156 Default(PlaneName),
157}
158
159#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
160#[ts(export, export_to = "FrontendApi.ts", rename = "ApiFace")]
161#[serde(rename_all = "camelCase")]
162pub struct Face {
163 pub id: ObjectId,
164}
165
166#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
167#[ts(export, export_to = "FrontendApi.ts", rename = "ApiSourceRef")]
168#[serde(tag = "type")]
169pub enum SourceRef {
170 Simple { range: SourceRange },
171 BackTrace { ranges: Vec<SourceRange> },
172}
173
174impl From<SourceRange> for SourceRef {
175 fn from(value: SourceRange) -> Self {
176 Self::Simple { range: value }
177 }
178}
179
180#[derive(Debug, Clone, Copy, PartialEq, Deserialize, Serialize, ts_rs::TS)]
181#[ts(export, export_to = "FrontendApi.ts")]
182pub struct Number {
183 pub value: f64,
184 pub units: NumericSuffix,
185}
186
187impl TryFrom<crate::std::args::TyF64> for Number {
188 type Error = crate::execution::types::NumericSuffixTypeConvertError;
189
190 fn try_from(value: crate::std::args::TyF64) -> std::result::Result<Self, Self::Error> {
191 Ok(Number {
192 value: value.n,
193 units: value.ty.try_into()?,
194 })
195 }
196}
197
198impl Number {
199 pub fn round(&self, digits: u8) -> Self {
200 let factor = 10f64.powi(digits as i32);
201 let rounded_value = (self.value * factor).round() / factor;
202 let value = if rounded_value == -0.0 { 0.0 } else { rounded_value };
204 Number {
205 value,
206 units: self.units,
207 }
208 }
209}
210
211#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
212#[ts(export, export_to = "FrontendApi.ts")]
213#[serde(tag = "type")]
214pub enum Expr {
215 Number(Number),
216 Var(Number),
217 Variable(String),
218}
219
220#[derive(Debug, Clone, Deserialize, Serialize, ts_rs::TS)]
221#[ts(export, export_to = "FrontendApi.ts")]
222pub struct Error {
223 pub msg: String,
224}
225
226impl Error {
227 pub fn file_id_in_use(id: FileId, path: &str) -> Self {
228 Error {
229 msg: format!("File ID already in use: {id:?}, currently used for `{path}`"),
230 }
231 }
232
233 pub fn bad_project(found: ProjectId, expected: Option<ProjectId>) -> Self {
234 let msg = match expected {
235 Some(expected) => format!("Project ID mismatch found: {found:?}, expected: {expected:?}"),
236 None => format!("No open project, found: {found:?}"),
237 };
238 Error { msg }
239 }
240
241 pub fn bad_version(found: Version, expected: Version) -> Self {
242 Error {
243 msg: format!("Version mismatch found: {found:?}, expected: {expected:?}"),
244 }
245 }
246
247 pub fn bad_file(found: FileId, expected: Option<FileId>) -> Self {
248 let msg = match expected {
249 Some(expected) => format!("File ID mismatch found: {found:?}, expected: {expected:?}"),
250 None => format!("File ID not found: {found:?}"),
251 };
252 Error { msg }
253 }
254
255 pub fn serialize(e: impl serde::ser::Error) -> Self {
256 Error {
257 msg: format!(
258 "Could not serialize successful KCL result. This is a bug in KCL and not in your code, please report this to Zoo. Details: {e}"
259 ),
260 }
261 }
262
263 pub fn deserialize(name: &str, e: impl serde::de::Error) -> Self {
264 Error {
265 msg: format!(
266 "Could not deserialize argument `{name}`. This is a bug in KCL and not in your code, please report this to Zoo. Details: {e}"
267 ),
268 }
269 }
270}
271
272pub type Result<T> = std::result::Result<T, Error>;