1use std::collections::HashMap;
2
3use serde::{Deserialize, Serialize};
4
5fn default_text_run_type() -> TextRunNodeType {
6 TextRunNodeType::Text
7}
8
9fn default_image_run_type() -> ImageRunNodeType {
10 ImageRunNodeType::Image
11}
12
13fn default_form_field_run_type() -> FormFieldRunNodeType {
14 FormFieldRunNodeType::FormField
15}
16
17fn default_paragraph_node_type() -> ParagraphNodeType {
18 ParagraphNodeType::Paragraph
19}
20
21fn default_table_cell_node_type() -> TableCellNodeType {
22 TableCellNodeType::TableCell
23}
24
25fn default_table_row_node_type() -> TableRowNodeType {
26 TableRowNodeType::TableRow
27}
28
29fn default_table_node_type() -> TableNodeType {
30 TableNodeType::Table
31}
32
33#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
35#[serde(rename_all = "camelCase")]
36pub enum ParagraphAlignment {
37 Left,
38 Center,
39 Right,
40 Justify,
41}
42
43#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
45#[serde(into = "i64")]
46pub enum HeadingLevel {
47 One = 1,
48 Two = 2,
49 Three = 3,
50 Four = 4,
51 Five = 5,
52 Six = 6,
53 Seven = 7,
54 Eight = 8,
55 Nine = 9,
56}
57
58impl<'de> Deserialize<'de> for HeadingLevel {
59 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
60 where
61 D: serde::Deserializer<'de>,
62 {
63 let value = i64::deserialize(deserializer)?;
64 match value {
65 1 => Ok(HeadingLevel::One),
66 2 => Ok(HeadingLevel::Two),
67 3 => Ok(HeadingLevel::Three),
68 4 => Ok(HeadingLevel::Four),
69 5 => Ok(HeadingLevel::Five),
70 6 => Ok(HeadingLevel::Six),
71 7 => Ok(HeadingLevel::Seven),
72 8 => Ok(HeadingLevel::Eight),
73 9 => Ok(HeadingLevel::Nine),
74 _ => Err(serde::de::Error::custom(format!(
75 "invalid heading level {value}"
76 ))),
77 }
78 }
79}
80
81impl From<HeadingLevel> for i64 {
82 fn from(value: HeadingLevel) -> Self {
83 value as i64
84 }
85}
86
87#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
89#[serde(rename_all = "camelCase")]
90pub struct TextRunBorderStyle {
91 #[serde(rename = "type")]
92 pub border_type: String,
93 #[serde(skip_serializing_if = "Option::is_none")]
94 pub color: Option<String>,
95 #[serde(skip_serializing_if = "Option::is_none")]
96 pub size_eighth_pt: Option<f64>,
97 #[serde(skip_serializing_if = "Option::is_none")]
98 pub space_pt: Option<f64>,
99 #[serde(skip_serializing_if = "Option::is_none")]
100 pub frame: Option<bool>,
101 #[serde(skip_serializing_if = "Option::is_none")]
102 pub shadow: Option<bool>,
103}
104
105#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
107#[serde(rename_all = "camelCase")]
108pub enum VerticalAlign {
109 #[serde(rename = "superscript")]
110 Superscript,
111 #[serde(rename = "subscript")]
112 Subscript,
113}
114
115#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
116#[serde(rename_all = "camelCase")]
117pub enum TextLigatures {
118 None,
119 Standard,
120 Contextual,
121 Historical,
122 Discretional,
123 StandardContextual,
124 StandardHistorical,
125 ContextualHistorical,
126 StandardDiscretional,
127 ContextualDiscretional,
128 HistoricalDiscretional,
129 StandardContextualHistorical,
130 StandardContextualDiscretional,
131 StandardHistoricalDiscretional,
132 ContextualHistoricalDiscretional,
133 All,
134}
135
136impl TextLigatures {
137 pub fn from_xml_value(value: &str) -> Option<Self> {
138 match value {
139 "none" => Some(Self::None),
140 "standard" => Some(Self::Standard),
141 "contextual" => Some(Self::Contextual),
142 "historical" => Some(Self::Historical),
143 "discretional" => Some(Self::Discretional),
144 "standardContextual" => Some(Self::StandardContextual),
145 "standardHistorical" => Some(Self::StandardHistorical),
146 "contextualHistorical" => Some(Self::ContextualHistorical),
147 "standardDiscretional" => Some(Self::StandardDiscretional),
148 "contextualDiscretional" => Some(Self::ContextualDiscretional),
149 "historicalDiscretional" => Some(Self::HistoricalDiscretional),
150 "standardContextualHistorical" => Some(Self::StandardContextualHistorical),
151 "standardContextualDiscretional" => Some(Self::StandardContextualDiscretional),
152 "standardHistoricalDiscretional" => Some(Self::StandardHistoricalDiscretional),
153 "contextualHistoricalDiscretional" => Some(Self::ContextualHistoricalDiscretional),
154 "all" => Some(Self::All),
155 _ => None,
156 }
157 }
158
159 pub fn xml_value(self) -> &'static str {
160 match self {
161 Self::None => "none",
162 Self::Standard => "standard",
163 Self::Contextual => "contextual",
164 Self::Historical => "historical",
165 Self::Discretional => "discretional",
166 Self::StandardContextual => "standardContextual",
167 Self::StandardHistorical => "standardHistorical",
168 Self::ContextualHistorical => "contextualHistorical",
169 Self::StandardDiscretional => "standardDiscretional",
170 Self::ContextualDiscretional => "contextualDiscretional",
171 Self::HistoricalDiscretional => "historicalDiscretional",
172 Self::StandardContextualHistorical => "standardContextualHistorical",
173 Self::StandardContextualDiscretional => "standardContextualDiscretional",
174 Self::StandardHistoricalDiscretional => "standardHistoricalDiscretional",
175 Self::ContextualHistoricalDiscretional => "contextualHistoricalDiscretional",
176 Self::All => "all",
177 }
178 }
179}
180
181#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
183#[serde(rename_all = "camelCase")]
184pub struct TextStyle {
185 #[serde(skip_serializing_if = "Option::is_none")]
186 pub bold: Option<bool>,
187 #[serde(skip_serializing_if = "Option::is_none")]
188 pub italic: Option<bool>,
189 #[serde(skip_serializing_if = "Option::is_none")]
190 pub underline: Option<bool>,
191 #[serde(skip_serializing_if = "Option::is_none")]
192 pub strike: Option<bool>,
193 #[serde(skip_serializing_if = "Option::is_none")]
194 pub all_caps: Option<bool>,
195 #[serde(skip_serializing_if = "Option::is_none")]
196 pub small_caps: Option<bool>,
197 #[serde(skip_serializing_if = "Option::is_none")]
198 pub color: Option<String>,
199 #[serde(skip_serializing_if = "Option::is_none")]
200 pub highlight: Option<String>,
201 #[serde(skip_serializing_if = "Option::is_none")]
202 pub background_color: Option<String>,
203 #[serde(skip_serializing_if = "Option::is_none")]
204 pub font_size_pt: Option<f64>,
205 #[serde(skip_serializing_if = "Option::is_none")]
206 pub font_size_cs_pt: Option<f64>,
207 #[serde(skip_serializing_if = "Option::is_none")]
208 pub font_family: Option<String>,
209 #[serde(skip_serializing_if = "Option::is_none")]
212 pub source_font_family: Option<String>,
213 #[serde(skip_serializing_if = "Option::is_none")]
216 pub font_family_ascii: Option<String>,
217 #[serde(skip_serializing_if = "Option::is_none")]
218 pub font_family_h_ansi: Option<String>,
219 #[serde(skip_serializing_if = "Option::is_none")]
220 pub font_family_east_asia: Option<String>,
221 #[serde(skip_serializing_if = "Option::is_none")]
222 pub font_family_cs: Option<String>,
223 #[serde(skip_serializing_if = "Option::is_none")]
224 pub font_theme_ascii: Option<String>,
225 #[serde(skip_serializing_if = "Option::is_none")]
226 pub font_theme_h_ansi: Option<String>,
227 #[serde(skip_serializing_if = "Option::is_none")]
228 pub font_theme_east_asia: Option<String>,
229 #[serde(skip_serializing_if = "Option::is_none")]
230 pub font_theme_cs: Option<String>,
231 #[serde(skip_serializing_if = "Option::is_none")]
232 pub resolved_font_family_ascii: Option<String>,
233 #[serde(skip_serializing_if = "Option::is_none")]
234 pub resolved_font_family_h_ansi: Option<String>,
235 #[serde(skip_serializing_if = "Option::is_none")]
236 pub resolved_font_family_east_asia: Option<String>,
237 #[serde(skip_serializing_if = "Option::is_none")]
238 pub resolved_font_family_cs: Option<String>,
239 #[serde(skip_serializing_if = "Option::is_none")]
240 pub font_hint: Option<String>,
241 #[serde(skip_serializing_if = "Option::is_none")]
244 pub language: Option<String>,
245 #[serde(skip_serializing_if = "Option::is_none")]
246 pub language_east_asia: Option<String>,
247 #[serde(skip_serializing_if = "Option::is_none")]
248 pub language_bidi: Option<String>,
249 #[serde(skip_serializing_if = "Option::is_none")]
250 pub right_to_left: Option<bool>,
251 #[serde(skip_serializing_if = "Option::is_none")]
252 pub complex_script: Option<bool>,
253 #[serde(skip_serializing_if = "Option::is_none")]
254 pub character_spacing_twips: Option<i64>,
255 #[serde(skip_serializing_if = "Option::is_none")]
256 pub character_scale_percent: Option<u16>,
257 #[serde(skip_serializing_if = "Option::is_none")]
258 pub source_character_scale: Option<String>,
259 #[serde(skip_serializing_if = "Option::is_none")]
260 pub kerning_min_font_size_pt: Option<f64>,
261 #[serde(skip_serializing_if = "Option::is_none")]
262 pub ligatures: Option<TextLigatures>,
263 #[serde(skip_serializing_if = "Option::is_none")]
264 pub contextual_alternates: Option<bool>,
265 #[serde(skip_serializing_if = "Option::is_none")]
266 pub vertical_align: Option<VerticalAlign>,
267 #[serde(skip_serializing_if = "Option::is_none")]
268 pub run_border: Option<TextRunBorderStyle>,
269}
270
271#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
273#[serde(transparent)]
274pub struct SharedTextStyle(std::sync::Arc<TextStyle>);
275
276impl SharedTextStyle {
277 pub fn into_owned(self) -> TextStyle {
278 std::sync::Arc::unwrap_or_clone(self.0)
279 }
280}
281
282impl From<TextStyle> for SharedTextStyle {
283 fn from(style: TextStyle) -> Self {
284 Self(std::sync::Arc::new(style))
285 }
286}
287
288impl std::ops::Deref for SharedTextStyle {
289 type Target = TextStyle;
290
291 fn deref(&self) -> &TextStyle {
292 &self.0
293 }
294}
295
296impl std::ops::DerefMut for SharedTextStyle {
297 fn deref_mut(&mut self) -> &mut TextStyle {
298 std::sync::Arc::make_mut(&mut self.0)
299 }
300}
301
302#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
304#[serde(rename_all = "camelCase")]
305pub struct NoteReference {
306 pub kind: NoteReferenceKind,
307 pub id: i64,
308}
309
310#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
312pub enum NoteReferenceKind {
313 #[serde(rename = "footnote")]
314 Footnote,
315 #[serde(rename = "endnote")]
316 Endnote,
317}
318
319#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
321#[serde(rename_all = "camelCase")]
322pub struct TextRunNode {
323 #[serde(default = "default_text_run_type", rename = "type")]
324 pub r#type: TextRunNodeType,
325 pub text: String,
326 #[serde(skip_serializing_if = "Option::is_none")]
327 pub style: Option<SharedTextStyle>,
328 #[serde(skip_serializing_if = "Option::is_none")]
329 pub link: Option<String>,
330 #[serde(skip_serializing_if = "Option::is_none")]
331 pub note_reference: Option<NoteReference>,
332}
333
334#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
336pub enum TextRunNodeType {
337 #[serde(rename = "text")]
338 Text,
339}
340
341#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
343#[serde(rename_all = "camelCase")]
344pub struct ImageCrop {
345 #[serde(skip_serializing_if = "Option::is_none")]
346 pub left_fraction: Option<f64>,
347 #[serde(skip_serializing_if = "Option::is_none")]
348 pub top_fraction: Option<f64>,
349 #[serde(skip_serializing_if = "Option::is_none")]
350 pub right_fraction: Option<f64>,
351 #[serde(skip_serializing_if = "Option::is_none")]
352 pub bottom_fraction: Option<f64>,
353}
354
355#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
357#[serde(rename_all = "camelCase")]
358pub enum ImageHorizontalAlign {
359 Left,
360 Center,
361 Right,
362 Inside,
363 Outside,
364}
365
366#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
368#[serde(rename_all = "camelCase")]
369pub enum ImageVerticalAlign {
370 Top,
371 Center,
372 Bottom,
373 Inside,
374 Outside,
375}
376
377#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
379#[serde(rename_all = "camelCase")]
380pub enum ImageWrapType {
381 #[serde(rename = "none")]
382 WrapNone,
383 #[serde(rename = "square")]
384 Square,
385 #[serde(rename = "tight")]
386 Tight,
387 #[serde(rename = "through")]
388 Through,
389 #[serde(rename = "topAndBottom")]
390 TopAndBottom,
391}
392
393#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
395#[serde(rename_all = "camelCase")]
396pub enum ImageWrapText {
397 #[serde(rename = "bothSides")]
398 BothSides,
399 #[serde(rename = "left")]
400 Left,
401 #[serde(rename = "right")]
402 Right,
403 #[serde(rename = "largest")]
404 Largest,
405}
406
407#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
409#[serde(rename_all = "camelCase")]
410pub struct ImageFloating {
411 #[serde(skip_serializing_if = "Option::is_none")]
412 pub x_px: Option<f64>,
413 #[serde(skip_serializing_if = "Option::is_none")]
414 pub y_px: Option<f64>,
415 #[serde(skip_serializing_if = "Option::is_none")]
416 pub horizontal_align: Option<ImageHorizontalAlign>,
417 #[serde(skip_serializing_if = "Option::is_none")]
418 pub vertical_align: Option<ImageVerticalAlign>,
419 #[serde(skip_serializing_if = "Option::is_none")]
420 pub horizontal_relative_to: Option<String>,
421 #[serde(skip_serializing_if = "Option::is_none")]
422 pub vertical_relative_to: Option<String>,
423 #[serde(skip_serializing_if = "Option::is_none")]
424 pub dist_l_px: Option<f64>,
425 #[serde(skip_serializing_if = "Option::is_none")]
426 pub dist_r_px: Option<f64>,
427 #[serde(skip_serializing_if = "Option::is_none")]
428 pub dist_t_px: Option<f64>,
429 #[serde(skip_serializing_if = "Option::is_none")]
430 pub dist_b_px: Option<f64>,
431 #[serde(skip_serializing_if = "Option::is_none")]
432 pub wrap_type: Option<ImageWrapType>,
433 #[serde(skip_serializing_if = "Option::is_none")]
434 pub wrap_text: Option<ImageWrapText>,
435 #[serde(skip_serializing_if = "Option::is_none")]
436 pub behind_document: Option<bool>,
437 #[serde(skip_serializing_if = "Option::is_none")]
438 pub z_index: Option<i64>,
439}
440
441#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
442#[serde(rename_all = "camelCase")]
443pub struct ImagePaintBounds {
444 pub x_px: f64,
445 pub y_px: f64,
446 pub width_px: f64,
447 pub height_px: f64,
448 pub layout_width_px: f64,
449 pub layout_height_px: f64,
450}
451
452#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
453#[serde(rename_all = "camelCase")]
454pub struct ImagePropertyAttribute {
455 pub name: String,
456 pub value: String,
457}
458
459#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
461#[serde(rename_all = "camelCase")]
462pub struct ImagePropertyNode {
463 pub name: String,
464 #[serde(default, skip_serializing_if = "Vec::is_empty")]
465 pub attributes: Vec<ImagePropertyAttribute>,
466 #[serde(default, skip_serializing_if = "Vec::is_empty")]
467 pub children: Vec<ImagePropertyNode>,
468 #[serde(default, skip_serializing_if = "Option::is_none")]
469 pub text: Option<String>,
470}
471
472#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
473#[serde(rename_all = "camelCase")]
474pub struct ImageAssetReference {
475 pub element: String,
476 pub kind: String,
477 #[serde(skip_serializing_if = "Option::is_none")]
478 pub relationship_id: Option<String>,
479 #[serde(skip_serializing_if = "Option::is_none")]
480 pub target: Option<String>,
481 pub embedded_asset_available: bool,
482}
483
484#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
485#[serde(rename_all = "camelCase")]
486pub struct ImageTransform {
487 #[serde(skip_serializing_if = "Option::is_none")]
488 pub rotation_degrees: Option<f64>,
489 #[serde(skip_serializing_if = "Option::is_none")]
490 pub flip_horizontal: Option<bool>,
491 #[serde(skip_serializing_if = "Option::is_none")]
492 pub flip_vertical: Option<bool>,
493 #[serde(skip_serializing_if = "Option::is_none")]
494 pub offset_x_emu: Option<f64>,
495 #[serde(skip_serializing_if = "Option::is_none")]
496 pub offset_y_emu: Option<f64>,
497 #[serde(skip_serializing_if = "Option::is_none")]
498 pub width_emu: Option<f64>,
499 #[serde(skip_serializing_if = "Option::is_none")]
500 pub height_emu: Option<f64>,
501}
502
503#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
504#[serde(rename_all = "camelCase")]
505pub struct ImageTile {
506 #[serde(skip_serializing_if = "Option::is_none")]
507 pub offset_x_emu: Option<f64>,
508 #[serde(skip_serializing_if = "Option::is_none")]
509 pub offset_y_emu: Option<f64>,
510 #[serde(skip_serializing_if = "Option::is_none")]
511 pub scale_x: Option<f64>,
512 #[serde(skip_serializing_if = "Option::is_none")]
513 pub scale_y: Option<f64>,
514 #[serde(skip_serializing_if = "Option::is_none")]
515 pub alignment: Option<String>,
516 #[serde(skip_serializing_if = "Option::is_none")]
517 pub flip: Option<String>,
518}
519
520#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
522#[serde(rename_all = "camelCase")]
523pub struct ImagePresentation {
524 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
525 pub transform_applied: bool,
526 #[serde(default, skip_serializing_if = "Vec::is_empty")]
527 pub assets: Vec<ImageAssetReference>,
528 #[serde(skip_serializing_if = "Option::is_none")]
529 pub transform: Option<ImageTransform>,
530 #[serde(skip_serializing_if = "Option::is_none")]
531 pub source_rectangle: Option<ImageCrop>,
532 #[serde(skip_serializing_if = "Option::is_none")]
533 pub fill_rectangle: Option<ImageCrop>,
534 #[serde(skip_serializing_if = "Option::is_none")]
535 pub tile: Option<ImageTile>,
536 #[serde(default, skip_serializing_if = "Vec::is_empty")]
537 pub properties: Vec<ImagePropertyNode>,
538}
539
540#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
542#[serde(rename_all = "camelCase")]
543pub struct ImageRunNode {
544 #[serde(default, skip_serializing_if = "Option::is_none")]
545 pub presentation: Option<Box<ImagePresentation>>,
546 #[serde(default, skip_serializing_if = "Option::is_none")]
547 pub chart_part_name: Option<String>,
548 #[serde(default, skip_serializing_if = "Option::is_none")]
549 pub chart_data: Option<Box<crate::ChartData>>,
550 #[serde(default, skip_serializing_if = "Option::is_none")]
551 pub paint_bounds: Option<Box<ImagePaintBounds>>,
552 #[serde(default, skip_serializing_if = "Option::is_none")]
553 pub math_ml: Option<String>,
554 #[serde(default, skip_serializing_if = "Option::is_none")]
555 pub content_nodes: Option<Vec<DocNode>>,
556 #[serde(default, skip_serializing_if = "Option::is_none")]
557 pub semantic_kind: Option<String>,
558 #[serde(default, skip_serializing_if = "Option::is_none")]
559 pub block_id: Option<String>,
560 #[serde(default = "default_image_run_type", rename = "type")]
561 pub r#type: ImageRunNodeType,
562 #[serde(skip_serializing_if = "Option::is_none")]
563 pub src: Option<String>,
564 #[serde(skip_serializing_if = "Option::is_none")]
565 pub alt: Option<String>,
566 #[serde(skip_serializing_if = "Option::is_none")]
567 pub width_px: Option<f64>,
568 #[serde(skip_serializing_if = "Option::is_none")]
569 pub height_px: Option<f64>,
570 #[serde(skip_serializing_if = "Option::is_none")]
571 pub part_name: Option<String>,
572 #[serde(skip_serializing_if = "Option::is_none")]
573 pub content_type: Option<String>,
574 #[serde(default, skip_serializing_if = "Option::is_none")]
575 pub data: Option<Vec<u8>>,
576 #[serde(skip_serializing_if = "Option::is_none")]
577 pub source_xml: Option<String>,
578 #[serde(skip_serializing_if = "Option::is_none")]
579 pub crop: Option<ImageCrop>,
580 #[serde(skip_serializing_if = "Option::is_none")]
581 pub css_filter: Option<String>,
582 #[serde(skip_serializing_if = "Option::is_none")]
583 pub css_opacity: Option<f64>,
584 #[serde(skip_serializing_if = "Option::is_none")]
585 pub floating: Option<ImageFloating>,
586 #[serde(skip_serializing_if = "Option::is_none")]
587 pub synthetic_text_box: Option<bool>,
588 #[serde(skip_serializing_if = "Option::is_none")]
589 pub text_box_text: Option<String>,
590}
591
592#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
594pub enum ImageRunNodeType {
595 #[serde(rename = "image")]
596 Image,
597}
598
599#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
601#[serde(rename_all = "camelCase")]
602pub enum FormFieldType {
603 #[serde(rename = "checkbox")]
604 Checkbox,
605 #[serde(rename = "text")]
606 Text,
607 #[serde(rename = "date")]
608 Date,
609 #[serde(rename = "dropdown")]
610 Dropdown,
611}
612
613#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
615pub enum FormFieldSourceKind {
616 #[serde(rename = "sdt")]
617 Sdt,
618 #[serde(rename = "legacy")]
619 Legacy,
620}
621
622#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
624#[serde(rename_all = "camelCase")]
625pub struct FormFieldOption {
626 pub display_text: String,
627 #[serde(skip_serializing_if = "Option::is_none")]
628 pub value: Option<String>,
629}
630
631#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
633#[serde(rename_all = "camelCase")]
634pub struct FormFieldTextWidgetSettings {
635 #[serde(skip_serializing_if = "Option::is_none")]
636 pub input_type: Option<String>,
637 #[serde(skip_serializing_if = "Option::is_none")]
638 pub default_text: Option<String>,
639 #[serde(skip_serializing_if = "Option::is_none")]
640 pub max_length: Option<i64>,
641 #[serde(skip_serializing_if = "Option::is_none")]
642 pub text_format: Option<String>,
643}
644
645#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
647#[serde(rename_all = "camelCase")]
648pub struct FormFieldCheckboxWidgetSettings {
649 #[serde(skip_serializing_if = "Option::is_none")]
650 pub default_checked: Option<bool>,
651 #[serde(skip_serializing_if = "Option::is_none")]
652 pub size_mode: Option<FormFieldCheckboxSizeMode>,
653 #[serde(skip_serializing_if = "Option::is_none")]
654 pub size_pt: Option<f64>,
655}
656
657#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
659pub enum FormFieldCheckboxSizeMode {
660 #[serde(rename = "auto")]
661 Auto,
662 #[serde(rename = "exact")]
663 Exact,
664}
665
666#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
668#[serde(rename_all = "camelCase")]
669pub struct FormFieldDropdownWidgetSettings {
670 #[serde(skip_serializing_if = "Option::is_none")]
671 pub default_value: Option<String>,
672}
673
674#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
676#[serde(rename_all = "camelCase")]
677pub struct FormFieldWidgetSettings {
678 #[serde(skip_serializing_if = "Option::is_none")]
679 pub name: Option<String>,
680 #[serde(skip_serializing_if = "Option::is_none")]
681 pub enabled: Option<bool>,
682 #[serde(skip_serializing_if = "Option::is_none")]
683 pub calc_on_exit: Option<bool>,
684 #[serde(skip_serializing_if = "Option::is_none")]
685 pub text: Option<FormFieldTextWidgetSettings>,
686 #[serde(skip_serializing_if = "Option::is_none")]
687 pub checkbox: Option<FormFieldCheckboxWidgetSettings>,
688 #[serde(skip_serializing_if = "Option::is_none")]
689 pub dropdown: Option<FormFieldDropdownWidgetSettings>,
690}
691
692#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
693#[serde(rename_all = "camelCase")]
694pub struct FormFieldSourcePreview {
695 pub relationship_id: String,
696 pub shape_id: String,
697 pub checked: bool,
698 pub image: Box<ImageRunNode>,
699}
700
701#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
703#[serde(rename_all = "camelCase")]
704pub struct FormFieldRunNode {
705 #[serde(default = "default_form_field_run_type", rename = "type")]
706 pub r#type: FormFieldRunNodeType,
707 pub field_type: FormFieldType,
708 #[serde(skip_serializing_if = "Option::is_none")]
709 pub source_kind: Option<FormFieldSourceKind>,
710 #[serde(skip_serializing_if = "Option::is_none")]
711 pub id: Option<i64>,
712 #[serde(skip_serializing_if = "Option::is_none")]
713 pub tag: Option<String>,
714 #[serde(skip_serializing_if = "Option::is_none")]
715 pub title: Option<String>,
716 #[serde(skip_serializing_if = "Option::is_none")]
717 pub placeholder: Option<String>,
718 #[serde(skip_serializing_if = "Option::is_none")]
719 pub checked: Option<bool>,
720 #[serde(skip_serializing_if = "Option::is_none")]
721 pub value: Option<String>,
722 #[serde(skip_serializing_if = "Option::is_none")]
723 pub options: Option<Vec<FormFieldOption>>,
724 #[serde(skip_serializing_if = "Option::is_none")]
725 pub widget: Option<FormFieldWidgetSettings>,
726 #[serde(skip_serializing_if = "Option::is_none")]
727 pub checked_symbol: Option<String>,
728 #[serde(skip_serializing_if = "Option::is_none")]
729 pub unchecked_symbol: Option<String>,
730 #[serde(skip_serializing_if = "Option::is_none")]
731 pub style: Option<SharedTextStyle>,
732 #[serde(skip_serializing_if = "Option::is_none")]
733 pub link: Option<String>,
734 #[serde(skip_serializing_if = "Option::is_none")]
735 pub source_xml: Option<String>,
736 #[serde(default, skip_serializing_if = "Option::is_none")]
737 pub source_preview: Option<Box<FormFieldSourcePreview>>,
738}
739
740#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
742pub enum FormFieldRunNodeType {
743 #[serde(rename = "form-field")]
744 FormField,
745}
746
747#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
749#[serde(untagged)]
750pub enum ParagraphChildNode {
751 Text(TextRunNode),
752 Image(Box<ImageRunNode>),
753 FormField(Box<FormFieldRunNode>),
754}
755
756#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
758#[serde(rename_all = "camelCase")]
759pub struct ParagraphNumbering {
760 pub num_id: i64,
761 pub ilvl: i64,
762}
763
764#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
766pub enum ParagraphLineRule {
767 #[serde(rename = "auto")]
768 Auto,
769 #[serde(rename = "exact")]
770 Exact,
771 #[serde(rename = "atLeast")]
772 AtLeast,
773}
774
775#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
777#[serde(rename_all = "camelCase")]
778pub struct ParagraphSpacing {
779 #[serde(skip_serializing_if = "Option::is_none")]
780 pub before_twips: Option<i64>,
781 #[serde(skip_serializing_if = "Option::is_none")]
782 pub after_twips: Option<i64>,
783 #[serde(skip_serializing_if = "Option::is_none")]
784 pub before_auto_spacing: Option<bool>,
785 #[serde(skip_serializing_if = "Option::is_none")]
786 pub after_auto_spacing: Option<bool>,
787 #[serde(skip_serializing_if = "Option::is_none")]
788 pub line_twips: Option<i64>,
789 #[serde(skip_serializing_if = "Option::is_none")]
790 pub line_rule: Option<ParagraphLineRule>,
791}
792
793#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
795#[serde(rename_all = "camelCase")]
796pub struct ParagraphIndent {
797 #[serde(skip_serializing_if = "Option::is_none")]
798 pub left_twips: Option<i64>,
799 #[serde(skip_serializing_if = "Option::is_none")]
800 pub right_twips: Option<i64>,
801 #[serde(skip_serializing_if = "Option::is_none")]
802 pub first_line_twips: Option<i64>,
803 #[serde(skip_serializing_if = "Option::is_none")]
804 pub hanging_twips: Option<i64>,
805}
806
807#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
809#[serde(rename_all = "camelCase")]
810pub struct ParagraphBorderStyle {
811 #[serde(rename = "type")]
812 pub border_type: String,
813 #[serde(skip_serializing_if = "Option::is_none")]
814 pub color: Option<String>,
815 #[serde(skip_serializing_if = "Option::is_none")]
816 pub size_eighth_pt: Option<f64>,
817 #[serde(skip_serializing_if = "Option::is_none")]
818 pub space_pt: Option<f64>,
819 #[serde(skip_serializing_if = "Option::is_none")]
820 pub frame: Option<bool>,
821 #[serde(skip_serializing_if = "Option::is_none")]
822 pub shadow: Option<bool>,
823}
824
825#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
827#[serde(rename_all = "camelCase")]
828pub struct ParagraphBorderSet {
829 #[serde(skip_serializing_if = "Option::is_none")]
830 pub top: Option<ParagraphBorderStyle>,
831 #[serde(skip_serializing_if = "Option::is_none")]
832 pub right: Option<ParagraphBorderStyle>,
833 #[serde(skip_serializing_if = "Option::is_none")]
834 pub bottom: Option<ParagraphBorderStyle>,
835 #[serde(skip_serializing_if = "Option::is_none")]
836 pub left: Option<ParagraphBorderStyle>,
837 #[serde(skip_serializing_if = "Option::is_none")]
838 pub between: Option<ParagraphBorderStyle>,
839 #[serde(skip_serializing_if = "Option::is_none")]
840 pub bar: Option<ParagraphBorderStyle>,
841}
842
843#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
845#[serde(rename_all = "camelCase")]
846pub enum ParagraphTabStopAlignment {
847 Clear,
848 Left,
849 Center,
850 Right,
851 Decimal,
852 Bar,
853}
854
855#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
857pub enum ParagraphTabStopLeader {
858 #[serde(rename = "none")]
859 LeaderNone,
860 #[serde(rename = "dot")]
861 Dot,
862 #[serde(rename = "hyphen")]
863 Hyphen,
864 #[serde(rename = "underscore")]
865 Underscore,
866 #[serde(rename = "middleDot")]
867 MiddleDot,
868}
869
870#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
872#[serde(rename_all = "camelCase")]
873pub struct ParagraphTabStop {
874 #[serde(skip_serializing_if = "Option::is_none")]
875 pub alignment: Option<ParagraphTabStopAlignment>,
876 #[serde(skip_serializing_if = "Option::is_none")]
877 pub leader: Option<ParagraphTabStopLeader>,
878 #[serde(skip_serializing_if = "Option::is_none")]
879 pub position_twips: Option<i64>,
880}
881
882#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
884pub enum ParagraphDropCapType {
885 #[serde(rename = "drop")]
886 Drop,
887 #[serde(rename = "margin")]
888 Margin,
889}
890
891#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
893#[serde(rename_all = "camelCase")]
894pub struct ParagraphDropCap {
895 #[serde(rename = "type")]
896 pub drop_cap_type: ParagraphDropCapType,
897 #[serde(skip_serializing_if = "Option::is_none")]
898 pub lines: Option<i64>,
899 #[serde(skip_serializing_if = "Option::is_none")]
900 pub wrap: Option<String>,
901 #[serde(skip_serializing_if = "Option::is_none")]
902 pub horizontal_anchor: Option<String>,
903 #[serde(skip_serializing_if = "Option::is_none")]
904 pub vertical_anchor: Option<String>,
905 #[serde(skip_serializing_if = "Option::is_none")]
906 pub x_twips: Option<i64>,
907 #[serde(skip_serializing_if = "Option::is_none")]
908 pub y_twips: Option<i64>,
909 #[serde(skip_serializing_if = "Option::is_none")]
910 pub horizontal_space_twips: Option<i64>,
911 #[serde(skip_serializing_if = "Option::is_none")]
912 pub vertical_space_twips: Option<i64>,
913}
914
915#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
916#[serde(rename_all = "camelCase")]
917pub enum ParagraphTextAlignment {
918 Auto,
919 Top,
920 Center,
921 Baseline,
922 Bottom,
923}
924
925#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
927#[serde(rename_all = "camelCase")]
928pub struct ParagraphStyle {
929 #[serde(skip_serializing_if = "Option::is_none")]
930 pub source_before_auto_spacing: Option<bool>,
931 #[serde(skip_serializing_if = "Option::is_none")]
932 pub source_after_auto_spacing: Option<bool>,
933 #[serde(skip_serializing_if = "Option::is_none")]
934 pub align: Option<ParagraphAlignment>,
935 #[serde(skip_serializing_if = "Option::is_none")]
936 pub snap_to_grid: Option<bool>,
937 #[serde(skip_serializing_if = "Option::is_none")]
938 pub source_snap_to_grid: Option<bool>,
939 #[serde(skip_serializing_if = "Option::is_none")]
940 pub source_has_snap_to_grid: Option<bool>,
941 #[serde(skip_serializing_if = "Option::is_none")]
942 pub source_inherited_snap_to_grid: Option<bool>,
943 #[serde(skip_serializing_if = "Option::is_none")]
944 pub source_table_snap_to_grid: Option<bool>,
945 #[serde(skip_serializing_if = "Option::is_none")]
946 pub text_alignment: Option<ParagraphTextAlignment>,
947 #[serde(skip_serializing_if = "Option::is_none")]
948 pub source_text_alignment: Option<ParagraphTextAlignment>,
949 #[serde(skip_serializing_if = "Option::is_none")]
950 pub source_has_text_alignment: Option<bool>,
951 #[serde(skip_serializing_if = "Option::is_none")]
952 pub source_inherited_text_alignment: Option<ParagraphTextAlignment>,
953 #[serde(skip_serializing_if = "Option::is_none")]
954 pub source_table_text_alignment: Option<ParagraphTextAlignment>,
955 #[serde(skip_serializing_if = "Option::is_none")]
956 pub heading_level: Option<HeadingLevel>,
957 #[serde(skip_serializing_if = "Option::is_none")]
958 pub style_id: Option<String>,
959 #[serde(skip_serializing_if = "Option::is_none")]
960 pub style_name: Option<String>,
961 #[serde(skip_serializing_if = "Option::is_none")]
962 pub numbering: Option<ParagraphNumbering>,
963 #[serde(skip_serializing_if = "Option::is_none")]
964 pub spacing: Option<ParagraphSpacing>,
965 #[serde(skip_serializing_if = "Option::is_none")]
966 pub indent: Option<ParagraphIndent>,
967 #[serde(skip_serializing_if = "Option::is_none")]
968 pub background_color: Option<String>,
969 #[serde(skip_serializing_if = "Option::is_none")]
970 pub borders: Option<ParagraphBorderSet>,
971 #[serde(skip_serializing_if = "Option::is_none")]
972 pub tab_stops: Option<Vec<ParagraphTabStop>>,
973 #[serde(skip_serializing_if = "Option::is_none")]
974 pub contextual_spacing: Option<bool>,
975 #[serde(skip_serializing_if = "Option::is_none")]
976 pub keep_next: Option<bool>,
977 #[serde(skip_serializing_if = "Option::is_none")]
978 pub keep_lines: Option<bool>,
979 #[serde(skip_serializing_if = "Option::is_none")]
980 pub widow_control: Option<bool>,
981 #[serde(skip_serializing_if = "Option::is_none")]
982 pub page_break_before: Option<bool>,
983 #[serde(skip_serializing_if = "Option::is_none")]
984 pub drop_cap: Option<ParagraphDropCap>,
985}
986
987#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
989#[serde(rename_all = "camelCase")]
990pub struct ParagraphSourceTextPatchRun {
991 #[serde(skip_serializing_if = "Option::is_none")]
992 pub style: Option<SharedTextStyle>,
993 #[serde(skip_serializing_if = "Option::is_none")]
994 pub link: Option<String>,
995 #[serde(skip_serializing_if = "Option::is_none")]
996 pub note_reference: Option<NoteReference>,
997}
998
999#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1000#[serde(rename_all = "camelCase")]
1001pub struct ParagraphSourceTextPatch {
1002 pub runs: Vec<ParagraphSourceTextPatchRun>,
1003}
1004
1005#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1007#[serde(rename_all = "camelCase")]
1008pub struct ParagraphMarkFormattingSource {
1009 #[serde(skip_serializing_if = "Option::is_none")]
1010 pub default_style: Option<SharedTextStyle>,
1011 #[serde(skip_serializing_if = "Option::is_none")]
1012 pub character_style: Option<SharedTextStyle>,
1013 #[serde(skip_serializing_if = "Option::is_none")]
1014 pub direct_style: Option<SharedTextStyle>,
1015}
1016
1017#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1019#[serde(rename_all = "camelCase")]
1020pub struct ParagraphNode {
1021 #[serde(default, skip_serializing_if = "Option::is_none")]
1022 pub review_content: Option<Vec<crate::revisions::ReviewContent>>,
1023 #[serde(default, skip_serializing_if = "Option::is_none")]
1024 pub block_id: Option<String>,
1025 #[serde(default = "default_paragraph_node_type", rename = "type")]
1026 pub r#type: ParagraphNodeType,
1027 pub children: Vec<ParagraphChildNode>,
1028 #[serde(skip_serializing_if = "Option::is_none")]
1029 pub style: Option<SharedParagraphStyle>,
1030 #[serde(skip_serializing_if = "Option::is_none")]
1031 pub paragraph_mark_style: Option<SharedTextStyle>,
1032 #[serde(skip_serializing_if = "Option::is_none")]
1033 pub source_paragraph_mark_style: Option<SharedTextStyle>,
1034 #[serde(skip_serializing_if = "Option::is_none")]
1035 pub source_paragraph_mark_formatting: Option<ParagraphMarkFormattingSource>,
1036 #[serde(skip_serializing_if = "Option::is_none")]
1037 pub source_paragraph_mark_properties_xml: Option<String>,
1038 #[serde(skip_serializing_if = "Option::is_none")]
1039 pub paragraph_mark_deleted: Option<bool>,
1040 #[serde(skip_serializing_if = "Option::is_none")]
1041 pub source_xml: Option<String>,
1042 #[serde(skip_serializing_if = "Option::is_none")]
1043 pub source_text_patch: Option<ParagraphSourceTextPatch>,
1044 #[serde(skip_serializing_if = "Option::is_none")]
1047 pub source_run_provenance: Option<ParagraphSourceTextPatch>,
1048}
1049
1050#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1052pub enum ParagraphNodeType {
1053 #[serde(rename = "paragraph")]
1054 Paragraph,
1055}
1056
1057#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1059#[serde(rename_all = "camelCase")]
1060pub struct TableBoxSpacing {
1061 #[serde(skip_serializing_if = "Option::is_none")]
1062 pub top_twips: Option<i64>,
1063 #[serde(skip_serializing_if = "Option::is_none")]
1064 pub right_twips: Option<i64>,
1065 #[serde(skip_serializing_if = "Option::is_none")]
1066 pub bottom_twips: Option<i64>,
1067 #[serde(skip_serializing_if = "Option::is_none")]
1068 pub left_twips: Option<i64>,
1069}
1070
1071#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1073#[serde(rename_all = "camelCase")]
1074pub struct TableBorderStyle {
1075 #[serde(rename = "type")]
1076 pub border_type: String,
1077 #[serde(skip_serializing_if = "Option::is_none")]
1078 pub color: Option<String>,
1079 #[serde(skip_serializing_if = "Option::is_none")]
1080 pub size_eighth_pt: Option<f64>,
1081}
1082
1083#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1085#[serde(rename_all = "camelCase")]
1086pub struct TableBorderSet {
1087 #[serde(skip_serializing_if = "Option::is_none")]
1088 pub top: Option<TableBorderStyle>,
1089 #[serde(skip_serializing_if = "Option::is_none")]
1090 pub right: Option<TableBorderStyle>,
1091 #[serde(skip_serializing_if = "Option::is_none")]
1092 pub bottom: Option<TableBorderStyle>,
1093 #[serde(skip_serializing_if = "Option::is_none")]
1094 pub left: Option<TableBorderStyle>,
1095 #[serde(skip_serializing_if = "Option::is_none")]
1096 pub inside_h: Option<TableBorderStyle>,
1097 #[serde(skip_serializing_if = "Option::is_none")]
1098 pub inside_v: Option<TableBorderStyle>,
1099 #[serde(skip_serializing_if = "Option::is_none")]
1100 pub tl2br: Option<TableBorderStyle>,
1101 #[serde(skip_serializing_if = "Option::is_none")]
1102 pub tr2bl: Option<TableBorderStyle>,
1103}
1104
1105#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1107#[serde(rename_all = "camelCase")]
1108pub enum TableCellVerticalAlign {
1109 Top,
1110 Center,
1111 Bottom,
1112}
1113
1114#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1116#[serde(tag = "type", rename_all = "camelCase")]
1117pub enum TablePreferredWidth {
1118 Dxa { value: f64 },
1119 Pct { value: f64 },
1120 Auto,
1121 Nil,
1122}
1123
1124#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1126#[serde(rename_all = "camelCase")]
1127pub struct TableWidthSource {
1128 #[serde(skip_serializing_if = "Option::is_none")]
1129 pub preferred_width: Option<TablePreferredWidth>,
1130 #[serde(skip_serializing_if = "Option::is_none")]
1131 pub width_twips: Option<i64>,
1132 #[serde(skip_serializing_if = "Option::is_none")]
1133 pub inherited_preferred_width: Option<TablePreferredWidth>,
1134}
1135
1136#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1137#[serde(rename_all = "camelCase")]
1138pub enum TableAlignment {
1139 Left,
1140 Center,
1141 Right,
1142}
1143
1144#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1146#[serde(rename_all = "camelCase")]
1147pub struct TableCellStyle {
1148 #[serde(skip_serializing_if = "Option::is_none")]
1149 pub background_color: Option<String>,
1150 #[serde(skip_serializing_if = "Option::is_none")]
1151 pub grid_span: Option<i64>,
1152 #[serde(skip_serializing_if = "Option::is_none")]
1153 pub row_span: Option<i64>,
1154 #[serde(skip_serializing_if = "Option::is_none")]
1155 pub v_merge_continuation: Option<bool>,
1156 #[serde(skip_serializing_if = "Option::is_none")]
1157 pub width_twips: Option<i64>,
1158 #[serde(skip_serializing_if = "Option::is_none")]
1159 pub preferred_width: Option<TablePreferredWidth>,
1160 #[serde(skip_serializing_if = "Option::is_none")]
1161 pub source_width: Option<TableWidthSource>,
1162 #[serde(skip_serializing_if = "Option::is_none")]
1163 pub margin_twips: Option<TableBoxSpacing>,
1164 #[serde(skip_serializing_if = "Option::is_none")]
1165 pub vertical_align: Option<TableCellVerticalAlign>,
1166 #[serde(skip_serializing_if = "Option::is_none")]
1167 pub text_direction: Option<String>,
1168 #[serde(skip_serializing_if = "Option::is_none")]
1169 pub source_text_direction: Option<String>,
1170 #[serde(skip_serializing_if = "Option::is_none")]
1171 pub no_wrap: Option<bool>,
1172 #[serde(skip_serializing_if = "Option::is_none")]
1173 pub source_no_wrap: Option<bool>,
1174 #[serde(skip_serializing_if = "Option::is_none")]
1175 pub borders: Option<TableBorderSet>,
1176}
1177
1178#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1180#[serde(untagged)]
1181pub enum TableCellContentNode {
1182 Paragraph(ParagraphNode),
1183 Table(Box<TableNode>),
1184}
1185
1186#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1188#[serde(rename_all = "camelCase")]
1189pub struct TableCellNode {
1190 #[serde(default = "default_table_cell_node_type", rename = "type")]
1191 pub r#type: TableCellNodeType,
1192 pub nodes: Vec<TableCellContentNode>,
1193 #[serde(skip_serializing_if = "Option::is_none")]
1194 pub style: Option<TableCellStyle>,
1195}
1196
1197#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1199pub enum TableCellNodeType {
1200 #[serde(rename = "table-cell")]
1201 TableCell,
1202}
1203
1204#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1206pub enum TableRowHeightRule {
1207 #[serde(rename = "auto")]
1208 Auto,
1209 #[serde(rename = "atLeast")]
1210 AtLeast,
1211 #[serde(rename = "exact")]
1212 Exact,
1213}
1214
1215#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1217#[serde(rename_all = "camelCase")]
1218pub struct TableRowGeometrySource {
1219 #[serde(skip_serializing_if = "Option::is_none")]
1220 pub grid_before: Option<i64>,
1221 #[serde(skip_serializing_if = "Option::is_none")]
1222 pub grid_after: Option<i64>,
1223 #[serde(skip_serializing_if = "Option::is_none")]
1224 pub width_before: Option<TablePreferredWidth>,
1225 #[serde(skip_serializing_if = "Option::is_none")]
1226 pub width_after: Option<TablePreferredWidth>,
1227}
1228
1229#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1231#[serde(rename_all = "camelCase")]
1232pub struct TableRowStyle {
1233 #[serde(skip_serializing_if = "Option::is_none")]
1234 pub grid_before: Option<i64>,
1235 #[serde(skip_serializing_if = "Option::is_none")]
1236 pub grid_after: Option<i64>,
1237 #[serde(skip_serializing_if = "Option::is_none")]
1238 pub width_before: Option<TablePreferredWidth>,
1239 #[serde(skip_serializing_if = "Option::is_none")]
1240 pub width_after: Option<TablePreferredWidth>,
1241 #[serde(skip_serializing_if = "Option::is_none")]
1242 pub source_row_geometry: Option<TableRowGeometrySource>,
1243 #[serde(skip_serializing_if = "Option::is_none")]
1244 pub background_color: Option<String>,
1245 #[serde(skip_serializing_if = "Option::is_none")]
1246 pub height_twips: Option<i64>,
1247 #[serde(skip_serializing_if = "Option::is_none")]
1248 pub height_rule: Option<TableRowHeightRule>,
1249 #[serde(skip_serializing_if = "Option::is_none")]
1250 pub cant_split: Option<bool>,
1251 #[serde(skip_serializing_if = "Option::is_none")]
1252 pub is_header: Option<bool>,
1253}
1254
1255#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1257#[serde(rename_all = "camelCase")]
1258pub struct TableRowNode {
1259 #[serde(default = "default_table_row_node_type", rename = "type")]
1260 pub r#type: TableRowNodeType,
1261 pub cells: Vec<TableCellNode>,
1262 #[serde(skip_serializing_if = "Option::is_none")]
1263 pub style: Option<TableRowStyle>,
1264}
1265
1266#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1268pub enum TableRowNodeType {
1269 #[serde(rename = "table-row")]
1270 TableRow,
1271}
1272
1273#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1275pub enum TableLayout {
1276 #[serde(rename = "fixed")]
1277 Fixed,
1278 #[serde(rename = "autofit")]
1279 Autofit,
1280}
1281
1282#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1284#[serde(rename_all = "camelCase")]
1285pub struct TableFloating {
1286 #[serde(skip_serializing_if = "Option::is_none")]
1287 pub x_twips: Option<i64>,
1288 #[serde(skip_serializing_if = "Option::is_none")]
1289 pub y_twips: Option<i64>,
1290 #[serde(skip_serializing_if = "Option::is_none")]
1291 pub left_from_text_twips: Option<i64>,
1292 #[serde(skip_serializing_if = "Option::is_none")]
1293 pub right_from_text_twips: Option<i64>,
1294 #[serde(skip_serializing_if = "Option::is_none")]
1295 pub top_from_text_twips: Option<i64>,
1296 #[serde(skip_serializing_if = "Option::is_none")]
1297 pub bottom_from_text_twips: Option<i64>,
1298 #[serde(skip_serializing_if = "Option::is_none")]
1299 pub horizontal_anchor: Option<String>,
1300 #[serde(skip_serializing_if = "Option::is_none")]
1301 pub vertical_anchor: Option<String>,
1302 #[serde(skip_serializing_if = "Option::is_none")]
1303 pub horizontal_align: Option<ImageHorizontalAlign>,
1304 #[serde(skip_serializing_if = "Option::is_none")]
1305 pub vertical_align: Option<ImageVerticalAlign>,
1306}
1307
1308#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1310#[serde(rename_all = "camelCase")]
1311pub struct TableStyle {
1312 #[serde(skip_serializing_if = "Option::is_none")]
1313 pub style_id: Option<String>,
1314 #[serde(skip_serializing_if = "Option::is_none")]
1315 pub source_style_id: Option<String>,
1316 #[serde(skip_serializing_if = "Option::is_none")]
1317 pub width_twips: Option<i64>,
1318 #[serde(skip_serializing_if = "Option::is_none")]
1319 pub preferred_width: Option<TablePreferredWidth>,
1320 #[serde(skip_serializing_if = "Option::is_none")]
1321 pub source_width: Option<TableWidthSource>,
1322 #[serde(skip_serializing_if = "Option::is_none")]
1323 pub alignment: Option<TableAlignment>,
1324 #[serde(skip_serializing_if = "Option::is_none")]
1325 pub source_alignment: Option<TableAlignment>,
1326 #[serde(skip_serializing_if = "Option::is_none")]
1327 pub source_inherited_alignment: Option<TableAlignment>,
1328 #[serde(skip_serializing_if = "Option::is_none")]
1329 pub bidi_visual: Option<bool>,
1330 #[serde(skip_serializing_if = "Option::is_none")]
1331 pub source_bidi_visual: Option<bool>,
1332 #[serde(skip_serializing_if = "Option::is_none")]
1333 pub source_inherited_bidi_visual: Option<bool>,
1334 #[serde(skip_serializing_if = "Option::is_none")]
1335 pub indent_twips: Option<i64>,
1336 #[serde(skip_serializing_if = "Option::is_none")]
1337 pub layout: Option<TableLayout>,
1338 #[serde(skip_serializing_if = "Option::is_none")]
1339 pub cell_spacing_twips: Option<i64>,
1340 #[serde(skip_serializing_if = "Option::is_none")]
1341 pub cell_margin_twips: Option<TableBoxSpacing>,
1342 #[serde(skip_serializing_if = "Option::is_none")]
1343 pub column_widths_twips: Option<Vec<i64>>,
1344 #[serde(skip_serializing_if = "Option::is_none")]
1347 pub collapsed_grid_columns: Option<Vec<usize>>,
1348 #[serde(skip_serializing_if = "Option::is_none")]
1349 pub borders: Option<TableBorderSet>,
1350 #[serde(skip_serializing_if = "Option::is_none")]
1351 pub floating: Option<TableFloating>,
1352}
1353
1354#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1356#[serde(rename_all = "camelCase")]
1357pub struct TableSourceTextPatch {
1358 pub source_paragraph_xml: String,
1359 pub paragraph: ParagraphNode,
1360}
1361
1362#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1364#[serde(rename_all = "camelCase")]
1365pub struct TableNode {
1366 #[serde(default, skip_serializing_if = "Option::is_none")]
1367 pub review_rows: Option<Vec<crate::revisions::ReviewRow>>,
1368 #[serde(default, skip_serializing_if = "Option::is_none")]
1369 pub block_id: Option<String>,
1370 #[serde(default = "default_table_node_type", rename = "type")]
1371 pub r#type: TableNodeType,
1372 pub rows: Vec<TableRowNode>,
1373 #[serde(skip_serializing_if = "Option::is_none")]
1374 pub style: Option<TableStyle>,
1375 #[serde(skip_serializing_if = "Option::is_none")]
1376 pub source_xml: Option<String>,
1377 #[serde(skip_serializing_if = "Option::is_none")]
1378 pub source_text_patches: Option<Vec<TableSourceTextPatch>>,
1379}
1380
1381#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1383pub enum TableNodeType {
1384 #[serde(rename = "table")]
1385 Table,
1386}
1387
1388#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1390#[serde(untagged)]
1391pub enum DocNode {
1392 Paragraph(ParagraphNode),
1393 Table(TableNode),
1394}
1395
1396#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1398#[serde(rename_all = "camelCase")]
1399pub struct HeaderSection {
1400 pub part_name: String,
1401 #[serde(skip_serializing_if = "Option::is_none")]
1402 pub reference_type: Option<String>,
1403 pub nodes: Vec<DocNode>,
1404}
1405
1406#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1408#[serde(rename_all = "camelCase")]
1409pub struct FooterSection {
1410 pub part_name: String,
1411 #[serde(skip_serializing_if = "Option::is_none")]
1412 pub reference_type: Option<String>,
1413 pub nodes: Vec<DocNode>,
1414}
1415
1416#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1418#[serde(rename_all = "camelCase")]
1419pub struct DocumentSection {
1420 pub start_node_index: i64,
1421 #[serde(skip_serializing_if = "Option::is_none")]
1422 pub section_properties_xml: Option<String>,
1423 pub header_sections: Vec<HeaderSection>,
1424 pub footer_sections: Vec<FooterSection>,
1425}
1426
1427#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1429#[serde(rename_all = "camelCase")]
1430pub struct ParagraphStyleDefinition {
1431 pub id: String,
1432 pub name: String,
1433 #[serde(skip_serializing_if = "Option::is_none")]
1434 pub based_on_id: Option<String>,
1435 #[serde(skip_serializing_if = "Option::is_none")]
1436 pub next_style_id: Option<String>,
1437 #[serde(skip_serializing_if = "Option::is_none")]
1438 pub align: Option<ParagraphAlignment>,
1439 #[serde(skip_serializing_if = "Option::is_none")]
1440 pub snap_to_grid: Option<bool>,
1441 #[serde(skip_serializing_if = "Option::is_none")]
1442 pub source_has_snap_to_grid: Option<bool>,
1443 #[serde(skip_serializing_if = "Option::is_none")]
1444 pub text_alignment: Option<ParagraphTextAlignment>,
1445 #[serde(skip_serializing_if = "Option::is_none")]
1446 pub source_has_text_alignment: Option<bool>,
1447 #[serde(skip_serializing_if = "Option::is_none")]
1448 pub heading_level: Option<HeadingLevel>,
1449 #[serde(skip_serializing_if = "Option::is_none")]
1450 pub numbering: Option<ParagraphNumbering>,
1451 #[serde(skip_serializing_if = "Option::is_none")]
1452 pub spacing: Option<ParagraphSpacing>,
1453 #[serde(skip_serializing_if = "Option::is_none")]
1454 pub indent: Option<ParagraphIndent>,
1455 #[serde(skip_serializing_if = "Option::is_none")]
1456 pub background_color: Option<String>,
1457 #[serde(skip_serializing_if = "Option::is_none")]
1458 pub borders: Option<ParagraphBorderSet>,
1459 #[serde(skip_serializing_if = "Option::is_none")]
1460 pub tab_stops: Option<Vec<ParagraphTabStop>>,
1461 #[serde(skip_serializing_if = "Option::is_none")]
1462 pub contextual_spacing: Option<bool>,
1463 #[serde(skip_serializing_if = "Option::is_none")]
1464 pub keep_next: Option<bool>,
1465 #[serde(skip_serializing_if = "Option::is_none")]
1466 pub keep_lines: Option<bool>,
1467 #[serde(skip_serializing_if = "Option::is_none")]
1468 pub widow_control: Option<bool>,
1469 #[serde(skip_serializing_if = "Option::is_none")]
1470 pub page_break_before: Option<bool>,
1471 #[serde(skip_serializing_if = "Option::is_none")]
1472 pub run_style: Option<TextStyle>,
1473 #[serde(skip_serializing_if = "Option::is_none")]
1474 pub ui_priority: Option<i64>,
1475 #[serde(skip_serializing_if = "Option::is_none")]
1476 pub is_default: Option<bool>,
1477 #[serde(skip_serializing_if = "Option::is_none")]
1478 pub is_primary: Option<bool>,
1479}
1480
1481#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1483pub enum NumberingLevelSuffix {
1484 #[serde(rename = "tab")]
1485 Tab,
1486 #[serde(rename = "space")]
1487 Space,
1488 #[serde(rename = "nothing")]
1489 Nothing,
1490}
1491
1492#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1494#[serde(rename_all = "camelCase")]
1495pub struct NumberingLevelDefinition {
1496 pub ilvl: i64,
1497 #[serde(skip_serializing_if = "Option::is_none")]
1498 pub start: Option<i64>,
1499 #[serde(skip_serializing_if = "Option::is_none")]
1500 pub format: Option<String>,
1501 #[serde(skip_serializing_if = "Option::is_none")]
1502 pub text: Option<String>,
1503 #[serde(skip_serializing_if = "Option::is_none")]
1504 pub suffix: Option<NumberingLevelSuffix>,
1505 #[serde(skip_serializing_if = "Option::is_none")]
1509 pub lvl_restart: Option<i64>,
1510 #[serde(skip_serializing_if = "Option::is_none")]
1513 pub is_lgl: Option<bool>,
1514 #[serde(skip_serializing_if = "Option::is_none")]
1515 pub indent: Option<ParagraphIndent>,
1516 #[serde(skip_serializing_if = "Option::is_none")]
1517 pub run_style: Option<TextStyle>,
1518 #[serde(skip_serializing_if = "Option::is_none")]
1519 pub bullet_font_family: Option<String>,
1520 #[serde(skip_serializing_if = "Option::is_none")]
1521 pub bullet_color: Option<String>,
1522 #[serde(skip_serializing_if = "Option::is_none")]
1523 pub picture_bullet_id: Option<i64>,
1524 #[serde(skip_serializing_if = "Option::is_none")]
1525 pub picture_bullet: Option<NumberingPictureBulletDefinition>,
1526}
1527
1528#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1530#[serde(rename_all = "camelCase")]
1531pub struct NumberingAbstractDefinition {
1532 pub abstract_num_id: i64,
1533 pub levels: Vec<NumberingLevelDefinition>,
1534}
1535
1536#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1538#[serde(rename_all = "camelCase")]
1539pub struct NumberingInstanceDefinition {
1540 pub num_id: i64,
1541 pub abstract_num_id: i64,
1542 #[serde(skip_serializing_if = "Option::is_none")]
1543 pub level_start_overrides: Option<HashMap<String, i64>>,
1544 #[serde(skip_serializing_if = "Option::is_none")]
1545 pub level_overrides: Option<Vec<NumberingLevelDefinition>>,
1546}
1547
1548#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1550#[serde(rename_all = "camelCase")]
1551pub struct NumberingDefinitionSet {
1552 pub abstracts: Vec<NumberingAbstractDefinition>,
1553 pub instances: Vec<NumberingInstanceDefinition>,
1554}
1555
1556#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1558#[serde(rename_all = "camelCase")]
1559pub struct NumberingPictureBulletDefinition {
1560 pub num_pic_bullet_id: i64,
1561 #[serde(skip_serializing_if = "Option::is_none")]
1562 pub src: Option<String>,
1563 #[serde(skip_serializing_if = "Option::is_none")]
1564 pub width_px: Option<f64>,
1565 #[serde(skip_serializing_if = "Option::is_none")]
1566 pub height_px: Option<f64>,
1567 #[serde(skip_serializing_if = "Option::is_none")]
1568 pub part_name: Option<String>,
1569 #[serde(skip_serializing_if = "Option::is_none")]
1570 pub content_type: Option<String>,
1571}
1572
1573#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1575#[serde(rename_all = "camelCase")]
1576pub struct DocumentNoteDefinition {
1577 pub id: i64,
1578 pub text: String,
1579 #[serde(skip_serializing_if = "Option::is_none")]
1580 pub nodes: Option<Vec<DocNode>>,
1581}
1582
1583#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1585#[serde(rename_all = "camelCase")]
1586pub struct DocumentCommentDefinition {
1587 #[serde(default, skip_serializing_if = "Option::is_none")]
1588 pub nodes: Option<Vec<DocNode>>,
1589 pub id: i64,
1590 #[serde(skip_serializing_if = "Option::is_none")]
1591 pub author: Option<String>,
1592 #[serde(skip_serializing_if = "Option::is_none")]
1593 pub initials: Option<String>,
1594 #[serde(skip_serializing_if = "Option::is_none")]
1595 pub date: Option<String>,
1596 pub text: String,
1597 #[serde(skip_serializing_if = "Option::is_none")]
1598 pub parent_id: Option<i64>,
1599 #[serde(skip_serializing_if = "Option::is_none")]
1600 pub resolved: Option<bool>,
1601 #[serde(skip_serializing_if = "Option::is_none")]
1602 pub source_xml: Option<String>,
1603 #[serde(skip_serializing_if = "Option::is_none")]
1604 pub extended_paragraph_id: Option<String>,
1605 #[serde(skip_serializing_if = "Option::is_none")]
1606 pub source_resolved: Option<bool>,
1607 #[serde(skip_serializing_if = "Option::is_none")]
1608 pub resolution_dirty: Option<bool>,
1609 #[serde(skip_serializing_if = "Option::is_none")]
1610 pub is_new: Option<bool>,
1611}
1612
1613#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1615#[serde(rename_all = "camelCase")]
1616pub struct DocumentCompatibilitySettings {
1617 #[serde(skip_serializing_if = "Option::is_none")]
1618 pub adjust_line_height_in_table: Option<bool>,
1619 #[serde(skip_serializing_if = "Option::is_none")]
1620 pub no_leading: Option<bool>,
1621 #[serde(
1622 rename = "suppressTopSpacingWP",
1623 skip_serializing_if = "Option::is_none"
1624 )]
1625 pub suppress_top_spacing_wp: Option<bool>,
1626 #[serde(
1627 rename = "truncateFontHeightsLikeWP6",
1628 skip_serializing_if = "Option::is_none"
1629 )]
1630 pub truncate_font_heights_like_wp6: Option<bool>,
1631 #[serde(skip_serializing_if = "Option::is_none")]
1632 pub no_extra_line_spacing: Option<bool>,
1633 #[serde(rename = "spaceForUL", skip_serializing_if = "Option::is_none")]
1634 pub space_for_ul: Option<bool>,
1635 #[serde(skip_serializing_if = "Option::is_none")]
1636 pub suppress_bottom_spacing: Option<bool>,
1637 #[serde(skip_serializing_if = "Option::is_none")]
1638 pub compatibility_mode: Option<i64>,
1639 #[serde(
1640 rename = "enableOpenTypeFeatures",
1641 skip_serializing_if = "Option::is_none"
1642 )]
1643 pub enable_open_type_features: Option<bool>,
1644 #[serde(skip_serializing_if = "Option::is_none")]
1645 pub suppress_spacing_before_after_page_break: Option<bool>,
1646 #[serde(skip_serializing_if = "Option::is_none")]
1647 pub use_printer_metrics: Option<bool>,
1648 #[serde(skip_serializing_if = "Option::is_none")]
1649 pub use_fixed_html_paragraph_spacing: Option<bool>,
1650 #[serde(skip_serializing_if = "Option::is_none")]
1651 pub do_not_break_wrapped_tables: Option<bool>,
1652 #[serde(skip_serializing_if = "Option::is_none")]
1653 pub do_not_break_constrained_forced_table: Option<bool>,
1654 #[serde(skip_serializing_if = "Option::is_none")]
1655 pub even_and_odd_headers: Option<bool>,
1656}
1657
1658#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1660#[serde(rename_all = "camelCase")]
1661pub struct DocModelMetadata {
1662 #[serde(default, skip_serializing_if = "Option::is_none")]
1663 pub document_id: Option<String>,
1664 #[serde(default, skip_serializing_if = "Option::is_none")]
1665 pub source_part_names: Option<std::collections::BTreeMap<String, String>>,
1666 pub source_parts: i64,
1667 pub warnings: Vec<String>,
1668 #[serde(skip_serializing_if = "Option::is_none")]
1669 pub document_page_count: Option<i64>,
1670 #[serde(skip_serializing_if = "Option::is_none")]
1671 pub document_open_tag: Option<String>,
1672 #[serde(skip_serializing_if = "Option::is_none")]
1673 pub document_background_color: Option<String>,
1674 #[serde(skip_serializing_if = "Option::is_none")]
1675 pub section_properties_xml: Option<String>,
1676 #[serde(skip_serializing_if = "Option::is_none")]
1677 pub sections: Option<Vec<DocumentSection>>,
1678 pub header_sections: Vec<HeaderSection>,
1679 pub footer_sections: Vec<FooterSection>,
1680 pub paragraph_styles: Vec<ParagraphStyleDefinition>,
1681 #[serde(skip_serializing_if = "Option::is_none")]
1682 pub default_paragraph_style_id: Option<String>,
1683 #[serde(skip_serializing_if = "Option::is_none")]
1684 pub default_tab_stop_twips: Option<i64>,
1685 #[serde(skip_serializing_if = "Option::is_none")]
1686 pub document_settings_imported: Option<bool>,
1687 #[serde(skip_serializing_if = "Option::is_none")]
1688 pub line_spacing_compatibility_imported: Option<bool>,
1689 #[serde(skip_serializing_if = "Option::is_none")]
1690 pub numbering_definitions: Option<NumberingDefinitionSet>,
1691 #[serde(skip_serializing_if = "Option::is_none")]
1692 pub compatibility: Option<DocumentCompatibilitySettings>,
1693 #[serde(skip_serializing_if = "Option::is_none")]
1694 pub footnotes: Option<Vec<DocumentNoteDefinition>>,
1695 #[serde(skip_serializing_if = "Option::is_none")]
1696 pub endnotes: Option<Vec<DocumentNoteDefinition>>,
1697 #[serde(skip_serializing_if = "Option::is_none")]
1698 pub comments: Option<Vec<DocumentCommentDefinition>>,
1699}
1700
1701#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1703#[serde(rename_all = "camelCase")]
1704pub struct DocModel {
1705 pub nodes: Vec<DocNode>,
1706 pub metadata: DocModelMetadata,
1707}
1708
1709#[cfg(test)]
1710mod deserialize_tests {
1711 use super::{DocModel, DocNode, HeadingLevel, ParagraphAlignment};
1712
1713 #[test]
1714 fn deserializes_minimal_js_doc_model() {
1715 let json = r#"{"nodes":[{"type":"paragraph","children":[{"type":"text","text":"Hi"}]}],"metadata":{"sourceParts":1,"warnings":[],"headerSections":[],"footerSections":[],"paragraphStyles":[]}}"#;
1716 let model: DocModel = serde_json::from_str(json).expect("should deserialize");
1717 assert_eq!(model.nodes.len(), 1);
1718 }
1719
1720 #[test]
1721 fn deserializes_heading_level_two() {
1722 let json = r#"{"type":"paragraph","style":{"headingLevel":2},"children":[{"type":"text","text":"Added"}]}"#;
1723 let node: DocNode = serde_json::from_str(json).expect("heading level 2");
1724 let DocNode::Paragraph(paragraph) = node else {
1725 panic!("expected paragraph");
1726 };
1727 assert_eq!(
1728 paragraph.style.and_then(|style| style.heading_level),
1729 Some(HeadingLevel::Two)
1730 );
1731 }
1732
1733 #[test]
1734 fn deserializes_align_center() {
1735 let json = r#"{"type":"paragraph","style":{"align":"center"},"children":[{"type":"text","text":"Added"}]}"#;
1736 let node: DocNode = serde_json::from_str(json).expect("align center");
1737 let DocNode::Paragraph(paragraph) = node else {
1738 panic!("expected paragraph");
1739 };
1740 assert_eq!(
1741 paragraph.style.and_then(|style| style.align),
1742 Some(ParagraphAlignment::Center)
1743 );
1744 }
1745}
1746
1747#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
1749#[serde(transparent)]
1750pub struct SharedParagraphStyle(std::sync::Arc<ParagraphStyle>);
1751
1752impl SharedParagraphStyle {
1753 pub fn into_owned(self) -> ParagraphStyle {
1754 std::sync::Arc::unwrap_or_clone(self.0)
1755 }
1756}
1757
1758impl From<ParagraphStyle> for SharedParagraphStyle {
1759 fn from(style: ParagraphStyle) -> Self {
1760 Self(std::sync::Arc::new(style))
1761 }
1762}
1763
1764impl std::ops::Deref for SharedParagraphStyle {
1765 type Target = ParagraphStyle;
1766
1767 fn deref(&self) -> &ParagraphStyle {
1768 &self.0
1769 }
1770}
1771
1772impl std::ops::DerefMut for SharedParagraphStyle {
1773 fn deref_mut(&mut self) -> &mut ParagraphStyle {
1774 std::sync::Arc::make_mut(&mut self.0)
1775 }
1776}