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