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
81#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd, Deserialize, Serialize, ts_rs::TS)]
82#[ts(export, export_to = "FrontendApi.ts", rename = "ApiVersion")]
83pub struct Version(pub usize);
84
85#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Deserialize, Serialize, ts_rs::TS)]
86#[ts(export, export_to = "FrontendApi.ts", rename = "ApiProjectId")]
87pub struct ProjectId(pub usize);
88
89#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Deserialize, Serialize, ts_rs::TS)]
90#[ts(export, export_to = "FrontendApi.ts", rename = "ApiFileId")]
91pub struct FileId(pub usize);
92
93#[derive(Debug, Clone, Deserialize, Serialize, ts_rs::TS)]
94#[ts(export, export_to = "FrontendApi.ts", rename = "ApiFile")]
95pub struct File {
96 pub id: FileId,
97 pub path: String,
98 pub text: String,
99}
100
101#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
102#[ts(export, export_to = "FrontendApi.ts", rename = "ApiObject")]
103pub struct Object {
104 pub id: ObjectId,
105 pub kind: ObjectKind,
106 pub label: String,
107 pub comments: String,
108 pub artifact_id: ArtifactId,
109 pub source: SourceRef,
110}
111
112impl Object {
113 pub fn placeholder(id: ObjectId, range: SourceRange) -> Self {
114 Object {
115 id,
116 kind: ObjectKind::Nil,
117 label: Default::default(),
118 comments: Default::default(),
119 artifact_id: ArtifactId::placeholder(),
120 source: SourceRef::Simple { range },
121 }
122 }
123}
124
125#[allow(clippy::large_enum_variant)]
126#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
127#[ts(export, export_to = "FrontendApi.ts", rename = "ApiObjectKind")]
128#[serde(tag = "type")]
129pub enum ObjectKind {
130 Nil,
132 Plane(Plane),
133 Sketch(crate::frontend::sketch::Sketch),
134 Segment {
137 segment: crate::frontend::sketch::Segment,
138 },
139 Constraint {
140 constraint: crate::frontend::sketch::Constraint,
141 },
142}
143
144#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
145#[ts(export, export_to = "FrontendApi.ts", rename = "ApiPlane")]
146#[serde(rename_all = "camelCase")]
147pub enum Plane {
148 Object(ObjectId),
149 Default(PlaneName),
150}
151
152#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
153#[ts(export, export_to = "FrontendApi.ts", rename = "ApiSourceRef")]
154#[serde(tag = "type")]
155pub enum SourceRef {
156 Simple { range: SourceRange },
157 BackTrace { ranges: Vec<SourceRange> },
158}
159
160impl From<SourceRange> for SourceRef {
161 fn from(value: SourceRange) -> Self {
162 Self::Simple { range: value }
163 }
164}
165
166#[derive(Debug, Clone, Copy, PartialEq, Deserialize, Serialize, ts_rs::TS)]
167#[ts(export, export_to = "FrontendApi.ts")]
168pub struct Number {
169 pub value: f64,
170 pub units: NumericSuffix,
171}
172
173impl TryFrom<crate::std::args::TyF64> for Number {
174 type Error = crate::execution::types::NumericSuffixTypeConvertError;
175
176 fn try_from(value: crate::std::args::TyF64) -> std::result::Result<Self, Self::Error> {
177 Ok(Number {
178 value: value.n,
179 units: value.ty.try_into()?,
180 })
181 }
182}
183
184impl Number {
185 pub fn round(&self, digits: u8) -> Self {
186 let factor = 10f64.powi(digits as i32);
187 let rounded_value = (self.value * factor).round() / factor;
188 Number {
189 value: rounded_value,
190 units: self.units,
191 }
192 }
193}
194
195#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
196#[ts(export, export_to = "FrontendApi.ts")]
197#[serde(tag = "type")]
198pub enum Expr {
199 Number(Number),
200 Var(Number),
201 Variable(String),
202}
203
204#[derive(Debug, Clone, Deserialize, Serialize, ts_rs::TS)]
205#[ts(export, export_to = "FrontendApi.ts")]
206pub struct Error {
207 pub msg: String,
208}
209
210impl Error {
211 pub fn file_id_in_use(id: FileId, path: &str) -> Self {
212 Error {
213 msg: format!("File ID already in use: {id:?}, currently used for `{path}`"),
214 }
215 }
216
217 pub fn bad_project(found: ProjectId, expected: Option<ProjectId>) -> Self {
218 let msg = match expected {
219 Some(expected) => format!("Project ID mismatch found: {found:?}, expected: {expected:?}"),
220 None => format!("No open project, found: {found:?}"),
221 };
222 Error { msg }
223 }
224
225 pub fn bad_version(found: Version, expected: Version) -> Self {
226 Error {
227 msg: format!("Version mismatch found: {found:?}, expected: {expected:?}"),
228 }
229 }
230
231 pub fn bad_file(found: FileId, expected: Option<FileId>) -> Self {
232 let msg = match expected {
233 Some(expected) => format!("File ID mismatch found: {found:?}, expected: {expected:?}"),
234 None => format!("File ID not found: {found:?}"),
235 };
236 Error { msg }
237 }
238
239 pub fn serialize(e: impl serde::ser::Error) -> Self {
240 Error {
241 msg: format!(
242 "Could not serialize successful KCL result. This is a bug in KCL and not in your code, please report this to Zoo. Details: {e}"
243 ),
244 }
245 }
246
247 pub fn deserialize(name: &str, e: impl serde::de::Error) -> Self {
248 Error {
249 msg: format!(
250 "Could not deserialize argument `{name}`. This is a bug in KCL and not in your code, please report this to Zoo. Details: {e}"
251 ),
252 }
253 }
254}
255
256pub type Result<T> = std::result::Result<T, Error>;