1use super::{
2 Color, Component, EasingType, Effect, File, LayoutConstraint, Paint, Rectangle, Styles,
3 TypeStyle,
4};
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Deserialize, Serialize, Copy, Clone)]
8#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
9#[typeshare::typeshare]
10pub enum StrokeAlign {
11 Inside,
13 Outside,
15 Center,
17}
18
19#[derive(Debug, Deserialize, Serialize)]
20#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
21#[typeshare::typeshare]
22pub enum LayoutMode {
23 None,
24 Horizontal,
25 Vertical,
26}
27
28#[derive(Debug, Deserialize, Serialize, PartialEq)]
29#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
30#[typeshare::typeshare]
31pub enum AxisSizingMode {
32 Fixed,
33 Auto,
34}
35
36#[derive(Debug, Deserialize, Serialize)]
37#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
38#[typeshare::typeshare]
39pub enum PrimaryAxisAlignItems {
40 Min,
41 Center,
42 Max,
43 SpaceBetween,
44}
45
46#[derive(Debug, Deserialize, Serialize)]
47#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
48#[typeshare::typeshare]
49pub enum CounterAxisAlignItems {
50 Min,
51 Center,
52 Max,
53 Baseline,
54}
55
56#[derive(Debug, Deserialize, Serialize)]
58#[typeshare::typeshare]
59pub struct StrokeWeights {
60 pub top: f64,
62 pub right: f64,
64 pub bottom: f64,
66 pub left: f64,
68}
69
70#[derive(Debug, Deserialize, Serialize)]
71#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
72#[typeshare::typeshare]
73pub enum LayoutAlign {
74 Inherit,
75 Stretch,
76 Min,
77 Center,
78 Max,
79}
80
81#[derive(Debug, Deserialize, Serialize)]
82#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
83#[typeshare::typeshare]
84pub enum LayoutPositioning {
85 Absolute,
86}
87
88#[derive(Debug, Deserialize, Serialize)]
90#[serde(rename_all = "camelCase")]
91#[typeshare::typeshare]
92pub struct Node {
93 pub id: String,
95 pub name: String,
97 #[serde(skip_serializing_if = "Option::is_none")]
99 pub visible: Option<bool>,
100 pub r#type: NodeType,
102 #[serde(skip_serializing_if = "Option::is_none")]
104 pub children: Option<Vec<Node>>,
105 #[serde(skip_serializing_if = "Option::is_none")]
107 pub background_color: Option<Color>,
108 #[serde(skip_serializing_if = "Option::is_none")]
110 pub fills: Option<Vec<Paint>>,
111 #[serde(skip_serializing_if = "Option::is_none")]
113 pub strokes: Option<Vec<Paint>>,
114 #[serde(skip_serializing_if = "Option::is_none")]
116 pub stroke_weight: Option<f64>,
117 #[serde(skip_serializing_if = "Option::is_none")]
119 pub individual_stroke_weights: Option<StrokeWeights>,
120 #[serde(skip_serializing_if = "Option::is_none")]
122 pub stroke_align: Option<StrokeAlign>,
123 #[serde(skip_serializing_if = "Option::is_none")]
125 pub stroke_dashes: Option<Vec<f64>>,
126 #[serde(skip_serializing_if = "Option::is_none")]
128 pub corner_radius: Option<f64>,
129 #[serde(skip_serializing_if = "Option::is_none")]
131 pub rectangle_corner_radii: Option<[f64; 4]>,
132 #[serde(skip_serializing_if = "Option::is_none")]
134 pub transition_duration: Option<f64>,
135 #[serde(skip_serializing_if = "Option::is_none")]
137 pub transition_easing: Option<EasingType>,
138 #[serde(skip_serializing_if = "Option::is_none")]
140 pub opacity: Option<f64>,
141 #[serde(skip_serializing_if = "Option::is_none")]
143 pub absolute_bounding_box: Option<Rectangle>,
144 #[serde(skip_serializing_if = "Option::is_none")]
146 pub absolute_render_bounds: Option<Rectangle>,
147 #[serde(skip_serializing_if = "Option::is_none")]
149 pub primary_axis_sizing_mode: Option<AxisSizingMode>,
150 #[serde(skip_serializing_if = "Option::is_none")]
152 pub counter_axis_sizing_mode: Option<AxisSizingMode>,
153 #[serde(skip_serializing_if = "Option::is_none")]
155 pub primary_axis_align_items: Option<PrimaryAxisAlignItems>,
156 #[serde(skip_serializing_if = "Option::is_none")]
158 pub counter_axis_align_items: Option<CounterAxisAlignItems>,
159 #[serde(skip_serializing_if = "Option::is_none")]
161 pub item_spacing: Option<f64>,
162 #[serde(skip_serializing_if = "Option::is_none")]
164 pub layout_positioning: Option<LayoutPositioning>,
165 #[serde(skip_serializing_if = "Option::is_none")]
167 pub layout_mode: Option<LayoutMode>,
168 #[serde(skip_serializing_if = "Option::is_none")]
170 pub padding_left: Option<f64>,
171 #[serde(skip_serializing_if = "Option::is_none")]
173 pub padding_right: Option<f64>,
174 #[serde(skip_serializing_if = "Option::is_none")]
176 pub padding_top: Option<f64>,
177 #[serde(skip_serializing_if = "Option::is_none")]
179 pub padding_bottom: Option<f64>,
180 #[serde(skip_serializing_if = "Option::is_none")]
182 pub effects: Option<Vec<Effect>>,
183 #[serde(skip_serializing_if = "Option::is_none")]
185 pub styles: Option<Styles>,
186 #[serde(skip_serializing_if = "Option::is_none")]
188 pub characters: Option<String>,
189 #[serde(skip_serializing_if = "Option::is_none")]
191 pub style: Option<TypeStyle>,
192 #[serde(skip_serializing_if = "Option::is_none")]
194 pub constraints: Option<LayoutConstraint>,
195 #[serde(skip_serializing_if = "Option::is_none")]
197 pub layout_align: Option<LayoutAlign>,
198 #[serde(skip_serializing_if = "Option::is_none")]
200 pub layout_grow: Option<f64>,
201}
202
203impl Node {
204 pub fn visible(&self) -> bool {
205 self.visible.unwrap_or(true)
206 }
207
208 pub fn background_color(&self) -> Option<&Color> {
209 self.background_color.as_ref()
210 }
211
212 pub fn absolute_bounding_box(&self) -> Option<&Rectangle> {
213 self.absolute_bounding_box.as_ref()
214 }
215
216 pub fn corner_radius(&self) -> Option<f64> {
217 self.corner_radius
218 }
219
220 pub fn rectangle_corner_radii(&self) -> Option<[f64; 4]> {
221 self.rectangle_corner_radii
222 .or_else(|| self.corner_radius.map(|r| [r, r, r, r]))
223 }
224
225 pub fn transition_duration(&self) -> Option<f64> {
226 self.transition_duration
227 }
228
229 pub fn transition_easing(&self) -> Option<&EasingType> {
230 self.transition_easing.as_ref()
231 }
232
233 pub fn opacity(&self) -> f64 {
234 self.opacity.unwrap_or(1.0)
235 }
236
237 pub fn padding_left(&self) -> f64 {
238 self.padding_left.unwrap_or(0.0)
239 }
240
241 pub fn padding_right(&self) -> f64 {
242 self.padding_right.unwrap_or(0.0)
243 }
244
245 pub fn padding_top(&self) -> f64 {
246 self.padding_top.unwrap_or(0.0)
247 }
248
249 pub fn padding_bottom(&self) -> f64 {
250 self.padding_bottom.unwrap_or(0.0)
251 }
252
253 pub fn children(&self) -> &[Node] {
254 self.children.as_deref().unwrap_or_default()
255 }
256
257 pub fn enabled_children(&self) -> impl Iterator<Item = &Node> {
258 self.children().iter().filter(|c| c.visible())
259 }
260
261 pub fn fills(&self) -> &[Paint] {
262 self.fills.as_deref().unwrap_or_default()
263 }
264
265 pub fn strokes(&self) -> &[Paint] {
266 self.strokes.as_deref().unwrap_or_default()
267 }
268
269 pub fn depth_first_stack_iter(&self) -> NodeDepthFirstStackIterator {
270 NodeDepthFirstStackIterator {
271 stack: vec![self],
272 iter_stack: vec![self.children().iter()],
273 }
274 }
275
276 pub fn component<'a>(&self, file: &'a File) -> Option<&'a Component> {
277 file.components.get(&self.id)
278 }
279
280 pub fn stroke_weight(&self) -> Option<f64> {
281 self.stroke_weight
282 }
283
284 pub fn stroke_align(&self) -> Option<&StrokeAlign> {
285 self.stroke_align.as_ref()
286 }
287}
288
289#[derive(Debug, Deserialize, Serialize, PartialEq, Copy, Clone)]
291#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
292#[typeshare::typeshare]
293pub enum NodeType {
294 Document,
295 Canvas,
296 Frame,
297 Group,
298 Vector,
299 BooleanOperation,
300 Star,
301 Line,
302 Ellipse,
303 RegularPolygon,
304 Rectangle,
305 Text,
306 Slice,
307 Component,
308 ComponentSet,
309 Instance,
310 Sticky,
311 ShapeWithText,
312 Connector,
313 Section,
314}
315
316pub struct NodeDepthFirstStackIterator<'a> {
317 iter_stack: Vec<std::slice::Iter<'a, Node>>,
318 stack: Vec<&'a Node>,
319}
320
321impl<'a> Iterator for NodeDepthFirstStackIterator<'a> {
322 type Item = (&'a Node, Vec<&'a Node>);
323
324 fn next(&mut self) -> Option<Self::Item> {
325 loop {
326 let mut bottom_of_iter_stack = self.iter_stack.pop()?;
327 let bottom_of_stack = self.stack.pop()?;
328 if let Some(current) = bottom_of_iter_stack.next() {
329 self.iter_stack.push(bottom_of_iter_stack);
330 self.iter_stack.push(current.children().iter());
331 self.stack.push(bottom_of_stack);
332 self.stack.push(current);
333 return Some((current, self.stack.clone()));
334 }
335 }
336 }
337}