Skip to main content

rust_docx/
model.rs

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/// Mirrors TypeScript `ParagraphAlignment`.
34#[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/// Mirrors TypeScript `HeadingLevel`.
44#[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/// Mirrors TypeScript `TextRunBorderStyle`.
88#[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/// Mirrors TypeScript `VerticalAlign`.
106#[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/// Mirrors TypeScript `TextStyle`.
182#[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    /// Import-time `font_family` value used to distinguish a later edit through
210    /// the backwards-compatible single-family API from the preserved slots.
211    #[serde(skip_serializing_if = "Option::is_none")]
212    pub source_font_family: Option<String>,
213    /// Explicit OOXML `w:rFonts` slots. `font_family` remains the resolved
214    /// single-family fallback used by existing renderers and callers.
215    #[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    /// OOXML `w:lang` slots plus run-direction flags that participate in font
242    /// selection for complex-script text.
243    #[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/// Fully resolved formatting shared until a caller edits its value.
272#[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/// Mirrors TypeScript `TextRunNode.noteReference`.
303#[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/// Mirrors TypeScript `TextRunNode.noteReference.kind`.
311#[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/// Mirrors TypeScript `TextRunNode`.
320#[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/// Discriminator for `TextRunNode`.
335#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
336pub enum TextRunNodeType {
337    #[serde(rename = "text")]
338    Text,
339}
340
341/// Mirrors TypeScript `ImageRunNode.crop`.
342#[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/// Mirrors TypeScript `ImageRunNode.floating.horizontalAlign`.
356#[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/// Mirrors TypeScript `ImageRunNode.floating.verticalAlign`.
367#[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/// Mirrors TypeScript `ImageRunNode.floating.wrapType`.
378#[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/// Mirrors TypeScript `ImageRunNode.floating.wrapText`.
394#[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/// Mirrors TypeScript `ImageRunNode.floating`.
408#[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/// Mirrors TypeScript `ImageRunNode`.
453#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
454#[serde(rename_all = "camelCase")]
455pub struct ImageRunNode {
456    #[serde(default, skip_serializing_if = "Option::is_none")]
457    pub chart_part_name: Option<String>,
458    #[serde(default, skip_serializing_if = "Option::is_none")]
459    pub chart_data: Option<Box<crate::ChartData>>,
460    #[serde(default, skip_serializing_if = "Option::is_none")]
461    pub paint_bounds: Option<Box<ImagePaintBounds>>,
462    #[serde(default, skip_serializing_if = "Option::is_none")]
463    pub math_ml: Option<String>,
464    #[serde(default, skip_serializing_if = "Option::is_none")]
465    pub content_nodes: Option<Vec<DocNode>>,
466    #[serde(default, skip_serializing_if = "Option::is_none")]
467    pub semantic_kind: Option<String>,
468    #[serde(default, skip_serializing_if = "Option::is_none")]
469    pub block_id: Option<String>,
470    #[serde(default = "default_image_run_type", rename = "type")]
471    pub r#type: ImageRunNodeType,
472    #[serde(skip_serializing_if = "Option::is_none")]
473    pub src: Option<String>,
474    #[serde(skip_serializing_if = "Option::is_none")]
475    pub alt: Option<String>,
476    #[serde(skip_serializing_if = "Option::is_none")]
477    pub width_px: Option<f64>,
478    #[serde(skip_serializing_if = "Option::is_none")]
479    pub height_px: Option<f64>,
480    #[serde(skip_serializing_if = "Option::is_none")]
481    pub part_name: Option<String>,
482    #[serde(skip_serializing_if = "Option::is_none")]
483    pub content_type: Option<String>,
484    #[serde(default, skip_serializing_if = "Option::is_none")]
485    pub data: Option<Vec<u8>>,
486    #[serde(skip_serializing_if = "Option::is_none")]
487    pub source_xml: Option<String>,
488    #[serde(skip_serializing_if = "Option::is_none")]
489    pub crop: Option<ImageCrop>,
490    #[serde(skip_serializing_if = "Option::is_none")]
491    pub css_filter: Option<String>,
492    #[serde(skip_serializing_if = "Option::is_none")]
493    pub css_opacity: Option<f64>,
494    #[serde(skip_serializing_if = "Option::is_none")]
495    pub floating: Option<ImageFloating>,
496    #[serde(skip_serializing_if = "Option::is_none")]
497    pub synthetic_text_box: Option<bool>,
498    #[serde(skip_serializing_if = "Option::is_none")]
499    pub text_box_text: Option<String>,
500}
501
502/// Discriminator for `ImageRunNode`.
503#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
504pub enum ImageRunNodeType {
505    #[serde(rename = "image")]
506    Image,
507}
508
509/// Mirrors TypeScript `FormFieldType`.
510#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
511#[serde(rename_all = "camelCase")]
512pub enum FormFieldType {
513    #[serde(rename = "checkbox")]
514    Checkbox,
515    #[serde(rename = "text")]
516    Text,
517    #[serde(rename = "date")]
518    Date,
519    #[serde(rename = "dropdown")]
520    Dropdown,
521}
522
523/// Mirrors TypeScript `FormFieldSourceKind`.
524#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
525pub enum FormFieldSourceKind {
526    #[serde(rename = "sdt")]
527    Sdt,
528    #[serde(rename = "legacy")]
529    Legacy,
530}
531
532/// Mirrors TypeScript `FormFieldOption`.
533#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
534#[serde(rename_all = "camelCase")]
535pub struct FormFieldOption {
536    pub display_text: String,
537    #[serde(skip_serializing_if = "Option::is_none")]
538    pub value: Option<String>,
539}
540
541/// Mirrors TypeScript `FormFieldTextWidgetSettings`.
542#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
543#[serde(rename_all = "camelCase")]
544pub struct FormFieldTextWidgetSettings {
545    #[serde(skip_serializing_if = "Option::is_none")]
546    pub input_type: Option<String>,
547    #[serde(skip_serializing_if = "Option::is_none")]
548    pub default_text: Option<String>,
549    #[serde(skip_serializing_if = "Option::is_none")]
550    pub max_length: Option<i64>,
551    #[serde(skip_serializing_if = "Option::is_none")]
552    pub text_format: Option<String>,
553}
554
555/// Mirrors TypeScript `FormFieldCheckboxWidgetSettings`.
556#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
557#[serde(rename_all = "camelCase")]
558pub struct FormFieldCheckboxWidgetSettings {
559    #[serde(skip_serializing_if = "Option::is_none")]
560    pub default_checked: Option<bool>,
561    #[serde(skip_serializing_if = "Option::is_none")]
562    pub size_mode: Option<FormFieldCheckboxSizeMode>,
563    #[serde(skip_serializing_if = "Option::is_none")]
564    pub size_pt: Option<f64>,
565}
566
567/// Mirrors TypeScript `FormFieldCheckboxWidgetSettings.sizeMode`.
568#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
569pub enum FormFieldCheckboxSizeMode {
570    #[serde(rename = "auto")]
571    Auto,
572    #[serde(rename = "exact")]
573    Exact,
574}
575
576/// Mirrors TypeScript `FormFieldDropdownWidgetSettings`.
577#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
578#[serde(rename_all = "camelCase")]
579pub struct FormFieldDropdownWidgetSettings {
580    #[serde(skip_serializing_if = "Option::is_none")]
581    pub default_value: Option<String>,
582}
583
584/// Mirrors TypeScript `FormFieldWidgetSettings`.
585#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
586#[serde(rename_all = "camelCase")]
587pub struct FormFieldWidgetSettings {
588    #[serde(skip_serializing_if = "Option::is_none")]
589    pub name: Option<String>,
590    #[serde(skip_serializing_if = "Option::is_none")]
591    pub enabled: Option<bool>,
592    #[serde(skip_serializing_if = "Option::is_none")]
593    pub calc_on_exit: Option<bool>,
594    #[serde(skip_serializing_if = "Option::is_none")]
595    pub text: Option<FormFieldTextWidgetSettings>,
596    #[serde(skip_serializing_if = "Option::is_none")]
597    pub checkbox: Option<FormFieldCheckboxWidgetSettings>,
598    #[serde(skip_serializing_if = "Option::is_none")]
599    pub dropdown: Option<FormFieldDropdownWidgetSettings>,
600}
601
602#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
603#[serde(rename_all = "camelCase")]
604pub struct FormFieldSourcePreview {
605    pub relationship_id: String,
606    pub shape_id: String,
607    pub checked: bool,
608    pub image: Box<ImageRunNode>,
609}
610
611/// Mirrors TypeScript `FormFieldRunNode`.
612#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
613#[serde(rename_all = "camelCase")]
614pub struct FormFieldRunNode {
615    #[serde(default = "default_form_field_run_type", rename = "type")]
616    pub r#type: FormFieldRunNodeType,
617    pub field_type: FormFieldType,
618    #[serde(skip_serializing_if = "Option::is_none")]
619    pub source_kind: Option<FormFieldSourceKind>,
620    #[serde(skip_serializing_if = "Option::is_none")]
621    pub id: Option<i64>,
622    #[serde(skip_serializing_if = "Option::is_none")]
623    pub tag: Option<String>,
624    #[serde(skip_serializing_if = "Option::is_none")]
625    pub title: Option<String>,
626    #[serde(skip_serializing_if = "Option::is_none")]
627    pub placeholder: Option<String>,
628    #[serde(skip_serializing_if = "Option::is_none")]
629    pub checked: Option<bool>,
630    #[serde(skip_serializing_if = "Option::is_none")]
631    pub value: Option<String>,
632    #[serde(skip_serializing_if = "Option::is_none")]
633    pub options: Option<Vec<FormFieldOption>>,
634    #[serde(skip_serializing_if = "Option::is_none")]
635    pub widget: Option<FormFieldWidgetSettings>,
636    #[serde(skip_serializing_if = "Option::is_none")]
637    pub checked_symbol: Option<String>,
638    #[serde(skip_serializing_if = "Option::is_none")]
639    pub unchecked_symbol: Option<String>,
640    #[serde(skip_serializing_if = "Option::is_none")]
641    pub style: Option<SharedTextStyle>,
642    #[serde(skip_serializing_if = "Option::is_none")]
643    pub link: Option<String>,
644    #[serde(skip_serializing_if = "Option::is_none")]
645    pub source_xml: Option<String>,
646    #[serde(default, skip_serializing_if = "Option::is_none")]
647    pub source_preview: Option<Box<FormFieldSourcePreview>>,
648}
649
650/// Discriminator for `FormFieldRunNode`.
651#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
652pub enum FormFieldRunNodeType {
653    #[serde(rename = "form-field")]
654    FormField,
655}
656
657/// Mirrors TypeScript `ParagraphChildNode`.
658#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
659#[serde(untagged)]
660pub enum ParagraphChildNode {
661    Text(TextRunNode),
662    Image(Box<ImageRunNode>),
663    FormField(Box<FormFieldRunNode>),
664}
665
666/// Mirrors TypeScript `ParagraphNumbering`.
667#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
668#[serde(rename_all = "camelCase")]
669pub struct ParagraphNumbering {
670    pub num_id: i64,
671    pub ilvl: i64,
672}
673
674/// Mirrors TypeScript `ParagraphSpacing.lineRule`.
675#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
676pub enum ParagraphLineRule {
677    #[serde(rename = "auto")]
678    Auto,
679    #[serde(rename = "exact")]
680    Exact,
681    #[serde(rename = "atLeast")]
682    AtLeast,
683}
684
685/// Mirrors TypeScript `ParagraphSpacing`.
686#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
687#[serde(rename_all = "camelCase")]
688pub struct ParagraphSpacing {
689    #[serde(skip_serializing_if = "Option::is_none")]
690    pub before_twips: Option<i64>,
691    #[serde(skip_serializing_if = "Option::is_none")]
692    pub after_twips: Option<i64>,
693    #[serde(skip_serializing_if = "Option::is_none")]
694    pub before_auto_spacing: Option<bool>,
695    #[serde(skip_serializing_if = "Option::is_none")]
696    pub after_auto_spacing: Option<bool>,
697    #[serde(skip_serializing_if = "Option::is_none")]
698    pub line_twips: Option<i64>,
699    #[serde(skip_serializing_if = "Option::is_none")]
700    pub line_rule: Option<ParagraphLineRule>,
701}
702
703/// Mirrors TypeScript `ParagraphIndent`.
704#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
705#[serde(rename_all = "camelCase")]
706pub struct ParagraphIndent {
707    #[serde(skip_serializing_if = "Option::is_none")]
708    pub left_twips: Option<i64>,
709    #[serde(skip_serializing_if = "Option::is_none")]
710    pub right_twips: Option<i64>,
711    #[serde(skip_serializing_if = "Option::is_none")]
712    pub first_line_twips: Option<i64>,
713    #[serde(skip_serializing_if = "Option::is_none")]
714    pub hanging_twips: Option<i64>,
715}
716
717/// Mirrors TypeScript `ParagraphBorderStyle`.
718#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
719#[serde(rename_all = "camelCase")]
720pub struct ParagraphBorderStyle {
721    #[serde(rename = "type")]
722    pub border_type: String,
723    #[serde(skip_serializing_if = "Option::is_none")]
724    pub color: Option<String>,
725    #[serde(skip_serializing_if = "Option::is_none")]
726    pub size_eighth_pt: Option<f64>,
727    #[serde(skip_serializing_if = "Option::is_none")]
728    pub space_pt: Option<f64>,
729    #[serde(skip_serializing_if = "Option::is_none")]
730    pub frame: Option<bool>,
731    #[serde(skip_serializing_if = "Option::is_none")]
732    pub shadow: Option<bool>,
733}
734
735/// Mirrors TypeScript `ParagraphBorderSet`.
736#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
737#[serde(rename_all = "camelCase")]
738pub struct ParagraphBorderSet {
739    #[serde(skip_serializing_if = "Option::is_none")]
740    pub top: Option<ParagraphBorderStyle>,
741    #[serde(skip_serializing_if = "Option::is_none")]
742    pub right: Option<ParagraphBorderStyle>,
743    #[serde(skip_serializing_if = "Option::is_none")]
744    pub bottom: Option<ParagraphBorderStyle>,
745    #[serde(skip_serializing_if = "Option::is_none")]
746    pub left: Option<ParagraphBorderStyle>,
747    #[serde(skip_serializing_if = "Option::is_none")]
748    pub between: Option<ParagraphBorderStyle>,
749    #[serde(skip_serializing_if = "Option::is_none")]
750    pub bar: Option<ParagraphBorderStyle>,
751}
752
753/// Mirrors TypeScript `ParagraphTabStop.alignment`.
754#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
755#[serde(rename_all = "camelCase")]
756pub enum ParagraphTabStopAlignment {
757    Clear,
758    Left,
759    Center,
760    Right,
761    Decimal,
762    Bar,
763}
764
765/// Mirrors TypeScript `ParagraphTabStop.leader`.
766#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
767pub enum ParagraphTabStopLeader {
768    #[serde(rename = "none")]
769    LeaderNone,
770    #[serde(rename = "dot")]
771    Dot,
772    #[serde(rename = "hyphen")]
773    Hyphen,
774    #[serde(rename = "underscore")]
775    Underscore,
776    #[serde(rename = "middleDot")]
777    MiddleDot,
778}
779
780/// Mirrors TypeScript `ParagraphTabStop`.
781#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
782#[serde(rename_all = "camelCase")]
783pub struct ParagraphTabStop {
784    #[serde(skip_serializing_if = "Option::is_none")]
785    pub alignment: Option<ParagraphTabStopAlignment>,
786    #[serde(skip_serializing_if = "Option::is_none")]
787    pub leader: Option<ParagraphTabStopLeader>,
788    #[serde(skip_serializing_if = "Option::is_none")]
789    pub position_twips: Option<i64>,
790}
791
792/// Mirrors TypeScript `ParagraphStyle.dropCap.type`.
793#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
794pub enum ParagraphDropCapType {
795    #[serde(rename = "drop")]
796    Drop,
797    #[serde(rename = "margin")]
798    Margin,
799}
800
801/// Mirrors TypeScript `ParagraphStyle.dropCap`.
802#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
803#[serde(rename_all = "camelCase")]
804pub struct ParagraphDropCap {
805    #[serde(rename = "type")]
806    pub drop_cap_type: ParagraphDropCapType,
807    #[serde(skip_serializing_if = "Option::is_none")]
808    pub lines: Option<i64>,
809    #[serde(skip_serializing_if = "Option::is_none")]
810    pub wrap: Option<String>,
811    #[serde(skip_serializing_if = "Option::is_none")]
812    pub horizontal_anchor: Option<String>,
813    #[serde(skip_serializing_if = "Option::is_none")]
814    pub vertical_anchor: Option<String>,
815    #[serde(skip_serializing_if = "Option::is_none")]
816    pub x_twips: Option<i64>,
817    #[serde(skip_serializing_if = "Option::is_none")]
818    pub y_twips: Option<i64>,
819    #[serde(skip_serializing_if = "Option::is_none")]
820    pub horizontal_space_twips: Option<i64>,
821    #[serde(skip_serializing_if = "Option::is_none")]
822    pub vertical_space_twips: Option<i64>,
823}
824
825#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
826#[serde(rename_all = "camelCase")]
827pub enum ParagraphTextAlignment {
828    Auto,
829    Top,
830    Center,
831    Baseline,
832    Bottom,
833}
834
835/// Mirrors TypeScript `ParagraphStyle`.
836#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
837#[serde(rename_all = "camelCase")]
838pub struct ParagraphStyle {
839    #[serde(skip_serializing_if = "Option::is_none")]
840    pub source_before_auto_spacing: Option<bool>,
841    #[serde(skip_serializing_if = "Option::is_none")]
842    pub source_after_auto_spacing: Option<bool>,
843    #[serde(skip_serializing_if = "Option::is_none")]
844    pub align: Option<ParagraphAlignment>,
845    #[serde(skip_serializing_if = "Option::is_none")]
846    pub snap_to_grid: Option<bool>,
847    #[serde(skip_serializing_if = "Option::is_none")]
848    pub source_snap_to_grid: Option<bool>,
849    #[serde(skip_serializing_if = "Option::is_none")]
850    pub source_has_snap_to_grid: Option<bool>,
851    #[serde(skip_serializing_if = "Option::is_none")]
852    pub source_inherited_snap_to_grid: Option<bool>,
853    #[serde(skip_serializing_if = "Option::is_none")]
854    pub source_table_snap_to_grid: Option<bool>,
855    #[serde(skip_serializing_if = "Option::is_none")]
856    pub text_alignment: Option<ParagraphTextAlignment>,
857    #[serde(skip_serializing_if = "Option::is_none")]
858    pub source_text_alignment: Option<ParagraphTextAlignment>,
859    #[serde(skip_serializing_if = "Option::is_none")]
860    pub source_has_text_alignment: Option<bool>,
861    #[serde(skip_serializing_if = "Option::is_none")]
862    pub source_inherited_text_alignment: Option<ParagraphTextAlignment>,
863    #[serde(skip_serializing_if = "Option::is_none")]
864    pub source_table_text_alignment: Option<ParagraphTextAlignment>,
865    #[serde(skip_serializing_if = "Option::is_none")]
866    pub heading_level: Option<HeadingLevel>,
867    #[serde(skip_serializing_if = "Option::is_none")]
868    pub style_id: Option<String>,
869    #[serde(skip_serializing_if = "Option::is_none")]
870    pub style_name: Option<String>,
871    #[serde(skip_serializing_if = "Option::is_none")]
872    pub numbering: Option<ParagraphNumbering>,
873    #[serde(skip_serializing_if = "Option::is_none")]
874    pub spacing: Option<ParagraphSpacing>,
875    #[serde(skip_serializing_if = "Option::is_none")]
876    pub indent: Option<ParagraphIndent>,
877    #[serde(skip_serializing_if = "Option::is_none")]
878    pub background_color: Option<String>,
879    #[serde(skip_serializing_if = "Option::is_none")]
880    pub borders: Option<ParagraphBorderSet>,
881    #[serde(skip_serializing_if = "Option::is_none")]
882    pub tab_stops: Option<Vec<ParagraphTabStop>>,
883    #[serde(skip_serializing_if = "Option::is_none")]
884    pub contextual_spacing: Option<bool>,
885    #[serde(skip_serializing_if = "Option::is_none")]
886    pub keep_next: Option<bool>,
887    #[serde(skip_serializing_if = "Option::is_none")]
888    pub keep_lines: Option<bool>,
889    #[serde(skip_serializing_if = "Option::is_none")]
890    pub widow_control: Option<bool>,
891    #[serde(skip_serializing_if = "Option::is_none")]
892    pub page_break_before: Option<bool>,
893    #[serde(skip_serializing_if = "Option::is_none")]
894    pub drop_cap: Option<ParagraphDropCap>,
895}
896
897/// Text-independent run provenance for a validated surgical source XML patch.
898#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
899#[serde(rename_all = "camelCase")]
900pub struct ParagraphSourceTextPatchRun {
901    #[serde(skip_serializing_if = "Option::is_none")]
902    pub style: Option<SharedTextStyle>,
903    #[serde(skip_serializing_if = "Option::is_none")]
904    pub link: Option<String>,
905    #[serde(skip_serializing_if = "Option::is_none")]
906    pub note_reference: Option<NoteReference>,
907}
908
909#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
910#[serde(rename_all = "camelCase")]
911pub struct ParagraphSourceTextPatch {
912    pub runs: Vec<ParagraphSourceTextPatchRun>,
913}
914
915/// Imported paragraph-mark default, character-style, and direct layers.
916#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
917#[serde(rename_all = "camelCase")]
918pub struct ParagraphMarkFormattingSource {
919    #[serde(skip_serializing_if = "Option::is_none")]
920    pub default_style: Option<SharedTextStyle>,
921    #[serde(skip_serializing_if = "Option::is_none")]
922    pub character_style: Option<SharedTextStyle>,
923    #[serde(skip_serializing_if = "Option::is_none")]
924    pub direct_style: Option<SharedTextStyle>,
925}
926
927/// Mirrors TypeScript `ParagraphNode`.
928#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
929#[serde(rename_all = "camelCase")]
930pub struct ParagraphNode {
931    #[serde(default, skip_serializing_if = "Option::is_none")]
932    pub block_id: Option<String>,
933    #[serde(default = "default_paragraph_node_type", rename = "type")]
934    pub r#type: ParagraphNodeType,
935    pub children: Vec<ParagraphChildNode>,
936    #[serde(skip_serializing_if = "Option::is_none")]
937    pub style: Option<SharedParagraphStyle>,
938    #[serde(skip_serializing_if = "Option::is_none")]
939    pub paragraph_mark_style: Option<SharedTextStyle>,
940    #[serde(skip_serializing_if = "Option::is_none")]
941    pub source_paragraph_mark_style: Option<SharedTextStyle>,
942    #[serde(skip_serializing_if = "Option::is_none")]
943    pub source_paragraph_mark_formatting: Option<ParagraphMarkFormattingSource>,
944    #[serde(skip_serializing_if = "Option::is_none")]
945    pub source_paragraph_mark_properties_xml: Option<String>,
946    #[serde(skip_serializing_if = "Option::is_none")]
947    pub paragraph_mark_deleted: Option<bool>,
948    #[serde(skip_serializing_if = "Option::is_none")]
949    pub source_xml: Option<String>,
950    #[serde(skip_serializing_if = "Option::is_none")]
951    pub source_text_patch: Option<ParagraphSourceTextPatch>,
952    /// Immutable import-time run metadata used to prove that a later surgical
953    /// annotation edit is still targeting the exact parsed run state.
954    #[serde(skip_serializing_if = "Option::is_none")]
955    pub source_run_provenance: Option<ParagraphSourceTextPatch>,
956}
957
958/// Discriminator for `ParagraphNode`.
959#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
960pub enum ParagraphNodeType {
961    #[serde(rename = "paragraph")]
962    Paragraph,
963}
964
965/// Mirrors TypeScript `TableBoxSpacing`.
966#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
967#[serde(rename_all = "camelCase")]
968pub struct TableBoxSpacing {
969    #[serde(skip_serializing_if = "Option::is_none")]
970    pub top_twips: Option<i64>,
971    #[serde(skip_serializing_if = "Option::is_none")]
972    pub right_twips: Option<i64>,
973    #[serde(skip_serializing_if = "Option::is_none")]
974    pub bottom_twips: Option<i64>,
975    #[serde(skip_serializing_if = "Option::is_none")]
976    pub left_twips: Option<i64>,
977}
978
979/// Mirrors TypeScript `TableBorderStyle`.
980#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
981#[serde(rename_all = "camelCase")]
982pub struct TableBorderStyle {
983    #[serde(rename = "type")]
984    pub border_type: String,
985    #[serde(skip_serializing_if = "Option::is_none")]
986    pub color: Option<String>,
987    #[serde(skip_serializing_if = "Option::is_none")]
988    pub size_eighth_pt: Option<f64>,
989}
990
991/// Mirrors TypeScript `TableBorderSet`.
992#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
993#[serde(rename_all = "camelCase")]
994pub struct TableBorderSet {
995    #[serde(skip_serializing_if = "Option::is_none")]
996    pub top: Option<TableBorderStyle>,
997    #[serde(skip_serializing_if = "Option::is_none")]
998    pub right: Option<TableBorderStyle>,
999    #[serde(skip_serializing_if = "Option::is_none")]
1000    pub bottom: Option<TableBorderStyle>,
1001    #[serde(skip_serializing_if = "Option::is_none")]
1002    pub left: Option<TableBorderStyle>,
1003    #[serde(skip_serializing_if = "Option::is_none")]
1004    pub inside_h: Option<TableBorderStyle>,
1005    #[serde(skip_serializing_if = "Option::is_none")]
1006    pub inside_v: Option<TableBorderStyle>,
1007    #[serde(skip_serializing_if = "Option::is_none")]
1008    pub tl2br: Option<TableBorderStyle>,
1009    #[serde(skip_serializing_if = "Option::is_none")]
1010    pub tr2bl: Option<TableBorderStyle>,
1011}
1012
1013/// Mirrors TypeScript `TableCellStyle.verticalAlign`.
1014#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1015#[serde(rename_all = "camelCase")]
1016pub enum TableCellVerticalAlign {
1017    Top,
1018    Center,
1019    Bottom,
1020}
1021
1022/// Preferred width with an explicit measurement kind.
1023#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1024#[serde(tag = "type", rename_all = "camelCase")]
1025pub enum TablePreferredWidth {
1026    Dxa { value: f64 },
1027    Pct { value: f64 },
1028    Auto,
1029    Nil,
1030}
1031
1032/// Imported width baseline used to distinguish edits to either width API.
1033#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1034#[serde(rename_all = "camelCase")]
1035pub struct TableWidthSource {
1036    #[serde(skip_serializing_if = "Option::is_none")]
1037    pub preferred_width: Option<TablePreferredWidth>,
1038    #[serde(skip_serializing_if = "Option::is_none")]
1039    pub width_twips: Option<i64>,
1040    #[serde(skip_serializing_if = "Option::is_none")]
1041    pub inherited_preferred_width: Option<TablePreferredWidth>,
1042}
1043
1044#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1045#[serde(rename_all = "camelCase")]
1046pub enum TableAlignment {
1047    Left,
1048    Center,
1049    Right,
1050}
1051
1052/// Mirrors TypeScript `TableCellStyle`.
1053#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1054#[serde(rename_all = "camelCase")]
1055pub struct TableCellStyle {
1056    #[serde(skip_serializing_if = "Option::is_none")]
1057    pub background_color: Option<String>,
1058    #[serde(skip_serializing_if = "Option::is_none")]
1059    pub grid_span: Option<i64>,
1060    #[serde(skip_serializing_if = "Option::is_none")]
1061    pub row_span: Option<i64>,
1062    #[serde(skip_serializing_if = "Option::is_none")]
1063    pub v_merge_continuation: Option<bool>,
1064    #[serde(skip_serializing_if = "Option::is_none")]
1065    pub width_twips: Option<i64>,
1066    #[serde(skip_serializing_if = "Option::is_none")]
1067    pub preferred_width: Option<TablePreferredWidth>,
1068    #[serde(skip_serializing_if = "Option::is_none")]
1069    pub source_width: Option<TableWidthSource>,
1070    #[serde(skip_serializing_if = "Option::is_none")]
1071    pub margin_twips: Option<TableBoxSpacing>,
1072    #[serde(skip_serializing_if = "Option::is_none")]
1073    pub vertical_align: Option<TableCellVerticalAlign>,
1074    #[serde(skip_serializing_if = "Option::is_none")]
1075    pub text_direction: Option<String>,
1076    #[serde(skip_serializing_if = "Option::is_none")]
1077    pub source_text_direction: Option<String>,
1078    #[serde(skip_serializing_if = "Option::is_none")]
1079    pub no_wrap: Option<bool>,
1080    #[serde(skip_serializing_if = "Option::is_none")]
1081    pub source_no_wrap: Option<bool>,
1082    #[serde(skip_serializing_if = "Option::is_none")]
1083    pub borders: Option<TableBorderSet>,
1084}
1085
1086/// Mirrors TypeScript `TableCellContentNode`.
1087#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1088#[serde(untagged)]
1089pub enum TableCellContentNode {
1090    Paragraph(ParagraphNode),
1091    Table(Box<TableNode>),
1092}
1093
1094/// Mirrors TypeScript `TableCellNode`.
1095#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1096#[serde(rename_all = "camelCase")]
1097pub struct TableCellNode {
1098    #[serde(default = "default_table_cell_node_type", rename = "type")]
1099    pub r#type: TableCellNodeType,
1100    pub nodes: Vec<TableCellContentNode>,
1101    #[serde(skip_serializing_if = "Option::is_none")]
1102    pub style: Option<TableCellStyle>,
1103}
1104
1105/// Discriminator for `TableCellNode`.
1106#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1107pub enum TableCellNodeType {
1108    #[serde(rename = "table-cell")]
1109    TableCell,
1110}
1111
1112/// Mirrors TypeScript `TableRowStyle.heightRule`.
1113#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1114pub enum TableRowHeightRule {
1115    #[serde(rename = "auto")]
1116    Auto,
1117    #[serde(rename = "atLeast")]
1118    AtLeast,
1119    #[serde(rename = "exact")]
1120    Exact,
1121}
1122
1123/// Imported row geometry used to distinguish a clear from an omitted legacy field.
1124#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1125#[serde(rename_all = "camelCase")]
1126pub struct TableRowGeometrySource {
1127    #[serde(skip_serializing_if = "Option::is_none")]
1128    pub grid_before: Option<i64>,
1129    #[serde(skip_serializing_if = "Option::is_none")]
1130    pub grid_after: Option<i64>,
1131    #[serde(skip_serializing_if = "Option::is_none")]
1132    pub width_before: Option<TablePreferredWidth>,
1133    #[serde(skip_serializing_if = "Option::is_none")]
1134    pub width_after: Option<TablePreferredWidth>,
1135}
1136
1137/// Mirrors TypeScript `TableRowStyle`.
1138#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1139#[serde(rename_all = "camelCase")]
1140pub struct TableRowStyle {
1141    #[serde(skip_serializing_if = "Option::is_none")]
1142    pub grid_before: Option<i64>,
1143    #[serde(skip_serializing_if = "Option::is_none")]
1144    pub grid_after: Option<i64>,
1145    #[serde(skip_serializing_if = "Option::is_none")]
1146    pub width_before: Option<TablePreferredWidth>,
1147    #[serde(skip_serializing_if = "Option::is_none")]
1148    pub width_after: Option<TablePreferredWidth>,
1149    #[serde(skip_serializing_if = "Option::is_none")]
1150    pub source_row_geometry: Option<TableRowGeometrySource>,
1151    #[serde(skip_serializing_if = "Option::is_none")]
1152    pub background_color: Option<String>,
1153    #[serde(skip_serializing_if = "Option::is_none")]
1154    pub height_twips: Option<i64>,
1155    #[serde(skip_serializing_if = "Option::is_none")]
1156    pub height_rule: Option<TableRowHeightRule>,
1157    #[serde(skip_serializing_if = "Option::is_none")]
1158    pub cant_split: Option<bool>,
1159    #[serde(skip_serializing_if = "Option::is_none")]
1160    pub is_header: Option<bool>,
1161}
1162
1163/// Mirrors TypeScript `TableRowNode`.
1164#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1165#[serde(rename_all = "camelCase")]
1166pub struct TableRowNode {
1167    #[serde(default = "default_table_row_node_type", rename = "type")]
1168    pub r#type: TableRowNodeType,
1169    pub cells: Vec<TableCellNode>,
1170    #[serde(skip_serializing_if = "Option::is_none")]
1171    pub style: Option<TableRowStyle>,
1172}
1173
1174/// Discriminator for `TableRowNode`.
1175#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1176pub enum TableRowNodeType {
1177    #[serde(rename = "table-row")]
1178    TableRow,
1179}
1180
1181/// Mirrors TypeScript `TableStyle.layout`.
1182#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1183pub enum TableLayout {
1184    #[serde(rename = "fixed")]
1185    Fixed,
1186    #[serde(rename = "autofit")]
1187    Autofit,
1188}
1189
1190/// Mirrors TypeScript `TableStyle.floating`.
1191#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1192#[serde(rename_all = "camelCase")]
1193pub struct TableFloating {
1194    #[serde(skip_serializing_if = "Option::is_none")]
1195    pub x_twips: Option<i64>,
1196    #[serde(skip_serializing_if = "Option::is_none")]
1197    pub y_twips: Option<i64>,
1198    #[serde(skip_serializing_if = "Option::is_none")]
1199    pub left_from_text_twips: Option<i64>,
1200    #[serde(skip_serializing_if = "Option::is_none")]
1201    pub right_from_text_twips: Option<i64>,
1202    #[serde(skip_serializing_if = "Option::is_none")]
1203    pub top_from_text_twips: Option<i64>,
1204    #[serde(skip_serializing_if = "Option::is_none")]
1205    pub bottom_from_text_twips: Option<i64>,
1206    #[serde(skip_serializing_if = "Option::is_none")]
1207    pub horizontal_anchor: Option<String>,
1208    #[serde(skip_serializing_if = "Option::is_none")]
1209    pub vertical_anchor: Option<String>,
1210    #[serde(skip_serializing_if = "Option::is_none")]
1211    pub horizontal_align: Option<ImageHorizontalAlign>,
1212    #[serde(skip_serializing_if = "Option::is_none")]
1213    pub vertical_align: Option<ImageVerticalAlign>,
1214}
1215
1216/// Mirrors TypeScript `TableStyle`.
1217#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1218#[serde(rename_all = "camelCase")]
1219pub struct TableStyle {
1220    #[serde(skip_serializing_if = "Option::is_none")]
1221    pub style_id: Option<String>,
1222    #[serde(skip_serializing_if = "Option::is_none")]
1223    pub source_style_id: Option<String>,
1224    #[serde(skip_serializing_if = "Option::is_none")]
1225    pub width_twips: Option<i64>,
1226    #[serde(skip_serializing_if = "Option::is_none")]
1227    pub preferred_width: Option<TablePreferredWidth>,
1228    #[serde(skip_serializing_if = "Option::is_none")]
1229    pub source_width: Option<TableWidthSource>,
1230    #[serde(skip_serializing_if = "Option::is_none")]
1231    pub alignment: Option<TableAlignment>,
1232    #[serde(skip_serializing_if = "Option::is_none")]
1233    pub source_alignment: Option<TableAlignment>,
1234    #[serde(skip_serializing_if = "Option::is_none")]
1235    pub source_inherited_alignment: Option<TableAlignment>,
1236    #[serde(skip_serializing_if = "Option::is_none")]
1237    pub bidi_visual: Option<bool>,
1238    #[serde(skip_serializing_if = "Option::is_none")]
1239    pub source_bidi_visual: Option<bool>,
1240    #[serde(skip_serializing_if = "Option::is_none")]
1241    pub source_inherited_bidi_visual: Option<bool>,
1242    #[serde(skip_serializing_if = "Option::is_none")]
1243    pub indent_twips: Option<i64>,
1244    #[serde(skip_serializing_if = "Option::is_none")]
1245    pub layout: Option<TableLayout>,
1246    #[serde(skip_serializing_if = "Option::is_none")]
1247    pub cell_spacing_twips: Option<i64>,
1248    #[serde(skip_serializing_if = "Option::is_none")]
1249    pub cell_margin_twips: Option<TableBoxSpacing>,
1250    #[serde(skip_serializing_if = "Option::is_none")]
1251    pub column_widths_twips: Option<Vec<i64>>,
1252    /// Logical intervals with coincident physical boundaries. Project these
1253    /// columns for layout and materialize them before structural edits.
1254    #[serde(skip_serializing_if = "Option::is_none")]
1255    pub collapsed_grid_columns: Option<Vec<usize>>,
1256    #[serde(skip_serializing_if = "Option::is_none")]
1257    pub borders: Option<TableBorderSet>,
1258    #[serde(skip_serializing_if = "Option::is_none")]
1259    pub floating: Option<TableFloating>,
1260}
1261
1262/// Text-only paragraph replacement retained inside an otherwise untouched table.
1263#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1264#[serde(rename_all = "camelCase")]
1265pub struct TableSourceTextPatch {
1266    pub source_paragraph_xml: String,
1267    pub paragraph: ParagraphNode,
1268}
1269
1270/// Mirrors TypeScript `TableNode`.
1271#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1272#[serde(rename_all = "camelCase")]
1273pub struct TableNode {
1274    #[serde(default, skip_serializing_if = "Option::is_none")]
1275    pub block_id: Option<String>,
1276    #[serde(default = "default_table_node_type", rename = "type")]
1277    pub r#type: TableNodeType,
1278    pub rows: Vec<TableRowNode>,
1279    #[serde(skip_serializing_if = "Option::is_none")]
1280    pub style: Option<TableStyle>,
1281    #[serde(skip_serializing_if = "Option::is_none")]
1282    pub source_xml: Option<String>,
1283    #[serde(skip_serializing_if = "Option::is_none")]
1284    pub source_text_patches: Option<Vec<TableSourceTextPatch>>,
1285}
1286
1287/// Discriminator for `TableNode`.
1288#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1289pub enum TableNodeType {
1290    #[serde(rename = "table")]
1291    Table,
1292}
1293
1294/// Mirrors TypeScript `DocNode`.
1295#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1296#[serde(untagged)]
1297pub enum DocNode {
1298    Paragraph(ParagraphNode),
1299    Table(TableNode),
1300}
1301
1302/// Mirrors TypeScript `HeaderSection`.
1303#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1304#[serde(rename_all = "camelCase")]
1305pub struct HeaderSection {
1306    pub part_name: String,
1307    #[serde(skip_serializing_if = "Option::is_none")]
1308    pub reference_type: Option<String>,
1309    pub nodes: Vec<DocNode>,
1310}
1311
1312/// Mirrors TypeScript `FooterSection`.
1313#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1314#[serde(rename_all = "camelCase")]
1315pub struct FooterSection {
1316    pub part_name: String,
1317    #[serde(skip_serializing_if = "Option::is_none")]
1318    pub reference_type: Option<String>,
1319    pub nodes: Vec<DocNode>,
1320}
1321
1322/// Mirrors TypeScript `DocumentSection`.
1323#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1324#[serde(rename_all = "camelCase")]
1325pub struct DocumentSection {
1326    pub start_node_index: i64,
1327    #[serde(skip_serializing_if = "Option::is_none")]
1328    pub section_properties_xml: Option<String>,
1329    pub header_sections: Vec<HeaderSection>,
1330    pub footer_sections: Vec<FooterSection>,
1331}
1332
1333/// Mirrors TypeScript `ParagraphStyleDefinition`.
1334#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1335#[serde(rename_all = "camelCase")]
1336pub struct ParagraphStyleDefinition {
1337    pub id: String,
1338    pub name: String,
1339    #[serde(skip_serializing_if = "Option::is_none")]
1340    pub based_on_id: Option<String>,
1341    #[serde(skip_serializing_if = "Option::is_none")]
1342    pub next_style_id: Option<String>,
1343    #[serde(skip_serializing_if = "Option::is_none")]
1344    pub align: Option<ParagraphAlignment>,
1345    #[serde(skip_serializing_if = "Option::is_none")]
1346    pub snap_to_grid: Option<bool>,
1347    #[serde(skip_serializing_if = "Option::is_none")]
1348    pub source_has_snap_to_grid: Option<bool>,
1349    #[serde(skip_serializing_if = "Option::is_none")]
1350    pub text_alignment: Option<ParagraphTextAlignment>,
1351    #[serde(skip_serializing_if = "Option::is_none")]
1352    pub source_has_text_alignment: Option<bool>,
1353    #[serde(skip_serializing_if = "Option::is_none")]
1354    pub heading_level: Option<HeadingLevel>,
1355    #[serde(skip_serializing_if = "Option::is_none")]
1356    pub numbering: Option<ParagraphNumbering>,
1357    #[serde(skip_serializing_if = "Option::is_none")]
1358    pub spacing: Option<ParagraphSpacing>,
1359    #[serde(skip_serializing_if = "Option::is_none")]
1360    pub indent: Option<ParagraphIndent>,
1361    #[serde(skip_serializing_if = "Option::is_none")]
1362    pub background_color: Option<String>,
1363    #[serde(skip_serializing_if = "Option::is_none")]
1364    pub borders: Option<ParagraphBorderSet>,
1365    #[serde(skip_serializing_if = "Option::is_none")]
1366    pub tab_stops: Option<Vec<ParagraphTabStop>>,
1367    #[serde(skip_serializing_if = "Option::is_none")]
1368    pub contextual_spacing: Option<bool>,
1369    #[serde(skip_serializing_if = "Option::is_none")]
1370    pub keep_next: Option<bool>,
1371    #[serde(skip_serializing_if = "Option::is_none")]
1372    pub keep_lines: Option<bool>,
1373    #[serde(skip_serializing_if = "Option::is_none")]
1374    pub widow_control: Option<bool>,
1375    #[serde(skip_serializing_if = "Option::is_none")]
1376    pub page_break_before: Option<bool>,
1377    #[serde(skip_serializing_if = "Option::is_none")]
1378    pub run_style: Option<TextStyle>,
1379    #[serde(skip_serializing_if = "Option::is_none")]
1380    pub ui_priority: Option<i64>,
1381    #[serde(skip_serializing_if = "Option::is_none")]
1382    pub is_default: Option<bool>,
1383    #[serde(skip_serializing_if = "Option::is_none")]
1384    pub is_primary: Option<bool>,
1385}
1386
1387/// Mirrors TypeScript `NumberingLevelDefinition.suffix`.
1388#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1389pub enum NumberingLevelSuffix {
1390    #[serde(rename = "tab")]
1391    Tab,
1392    #[serde(rename = "space")]
1393    Space,
1394    #[serde(rename = "nothing")]
1395    Nothing,
1396}
1397
1398/// Mirrors TypeScript `NumberingLevelDefinition`.
1399#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1400#[serde(rename_all = "camelCase")]
1401pub struct NumberingLevelDefinition {
1402    pub ilvl: i64,
1403    #[serde(skip_serializing_if = "Option::is_none")]
1404    pub start: Option<i64>,
1405    #[serde(skip_serializing_if = "Option::is_none")]
1406    pub format: Option<String>,
1407    #[serde(skip_serializing_if = "Option::is_none")]
1408    pub text: Option<String>,
1409    #[serde(skip_serializing_if = "Option::is_none")]
1410    pub suffix: Option<NumberingLevelSuffix>,
1411    /// `w:lvlRestart`: one-based level whose use restarts this level; 0 means
1412    /// this level never restarts. Absent means the OOXML default (restart when
1413    /// any shallower level is used).
1414    #[serde(skip_serializing_if = "Option::is_none")]
1415    pub lvl_restart: Option<i64>,
1416    /// `w:isLgl`: legal numbering — every placeholder in this level's text
1417    /// renders as decimal regardless of the referenced levels' formats.
1418    #[serde(skip_serializing_if = "Option::is_none")]
1419    pub is_lgl: Option<bool>,
1420    #[serde(skip_serializing_if = "Option::is_none")]
1421    pub indent: Option<ParagraphIndent>,
1422    #[serde(skip_serializing_if = "Option::is_none")]
1423    pub run_style: Option<TextStyle>,
1424    #[serde(skip_serializing_if = "Option::is_none")]
1425    pub bullet_font_family: Option<String>,
1426    #[serde(skip_serializing_if = "Option::is_none")]
1427    pub bullet_color: Option<String>,
1428    #[serde(skip_serializing_if = "Option::is_none")]
1429    pub picture_bullet_id: Option<i64>,
1430    #[serde(skip_serializing_if = "Option::is_none")]
1431    pub picture_bullet: Option<NumberingPictureBulletDefinition>,
1432}
1433
1434/// Mirrors TypeScript `NumberingAbstractDefinition`.
1435#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1436#[serde(rename_all = "camelCase")]
1437pub struct NumberingAbstractDefinition {
1438    pub abstract_num_id: i64,
1439    pub levels: Vec<NumberingLevelDefinition>,
1440}
1441
1442/// Mirrors TypeScript `NumberingInstanceDefinition`.
1443#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1444#[serde(rename_all = "camelCase")]
1445pub struct NumberingInstanceDefinition {
1446    pub num_id: i64,
1447    pub abstract_num_id: i64,
1448    #[serde(skip_serializing_if = "Option::is_none")]
1449    pub level_start_overrides: Option<HashMap<String, i64>>,
1450    #[serde(skip_serializing_if = "Option::is_none")]
1451    pub level_overrides: Option<Vec<NumberingLevelDefinition>>,
1452}
1453
1454/// Mirrors TypeScript `NumberingDefinitionSet`.
1455#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1456#[serde(rename_all = "camelCase")]
1457pub struct NumberingDefinitionSet {
1458    pub abstracts: Vec<NumberingAbstractDefinition>,
1459    pub instances: Vec<NumberingInstanceDefinition>,
1460}
1461
1462/// Mirrors TypeScript `NumberingPictureBulletDefinition`.
1463#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1464#[serde(rename_all = "camelCase")]
1465pub struct NumberingPictureBulletDefinition {
1466    pub num_pic_bullet_id: i64,
1467    #[serde(skip_serializing_if = "Option::is_none")]
1468    pub src: Option<String>,
1469    #[serde(skip_serializing_if = "Option::is_none")]
1470    pub width_px: Option<f64>,
1471    #[serde(skip_serializing_if = "Option::is_none")]
1472    pub height_px: Option<f64>,
1473    #[serde(skip_serializing_if = "Option::is_none")]
1474    pub part_name: Option<String>,
1475    #[serde(skip_serializing_if = "Option::is_none")]
1476    pub content_type: Option<String>,
1477}
1478
1479/// Mirrors TypeScript `DocumentNoteDefinition`.
1480#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1481#[serde(rename_all = "camelCase")]
1482pub struct DocumentNoteDefinition {
1483    pub id: i64,
1484    pub text: String,
1485    #[serde(skip_serializing_if = "Option::is_none")]
1486    pub nodes: Option<Vec<DocNode>>,
1487}
1488
1489/// Mirrors TypeScript `DocumentCommentDefinition`.
1490#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1491#[serde(rename_all = "camelCase")]
1492pub struct DocumentCommentDefinition {
1493    #[serde(default, skip_serializing_if = "Option::is_none")]
1494    pub nodes: Option<Vec<DocNode>>,
1495    pub id: i64,
1496    #[serde(skip_serializing_if = "Option::is_none")]
1497    pub author: Option<String>,
1498    #[serde(skip_serializing_if = "Option::is_none")]
1499    pub initials: Option<String>,
1500    #[serde(skip_serializing_if = "Option::is_none")]
1501    pub date: Option<String>,
1502    pub text: String,
1503    #[serde(skip_serializing_if = "Option::is_none")]
1504    pub parent_id: Option<i64>,
1505    #[serde(skip_serializing_if = "Option::is_none")]
1506    pub resolved: Option<bool>,
1507    #[serde(skip_serializing_if = "Option::is_none")]
1508    pub source_xml: Option<String>,
1509    #[serde(skip_serializing_if = "Option::is_none")]
1510    pub extended_paragraph_id: Option<String>,
1511    #[serde(skip_serializing_if = "Option::is_none")]
1512    pub source_resolved: Option<bool>,
1513    #[serde(skip_serializing_if = "Option::is_none")]
1514    pub resolution_dirty: Option<bool>,
1515    #[serde(skip_serializing_if = "Option::is_none")]
1516    pub is_new: Option<bool>,
1517}
1518
1519/// Mirrors TypeScript `DocumentCompatibilitySettings`.
1520#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1521#[serde(rename_all = "camelCase")]
1522pub struct DocumentCompatibilitySettings {
1523    #[serde(skip_serializing_if = "Option::is_none")]
1524    pub adjust_line_height_in_table: Option<bool>,
1525    #[serde(skip_serializing_if = "Option::is_none")]
1526    pub no_leading: Option<bool>,
1527    #[serde(
1528        rename = "suppressTopSpacingWP",
1529        skip_serializing_if = "Option::is_none"
1530    )]
1531    pub suppress_top_spacing_wp: Option<bool>,
1532    #[serde(
1533        rename = "truncateFontHeightsLikeWP6",
1534        skip_serializing_if = "Option::is_none"
1535    )]
1536    pub truncate_font_heights_like_wp6: Option<bool>,
1537    #[serde(skip_serializing_if = "Option::is_none")]
1538    pub no_extra_line_spacing: Option<bool>,
1539    #[serde(rename = "spaceForUL", skip_serializing_if = "Option::is_none")]
1540    pub space_for_ul: Option<bool>,
1541    #[serde(skip_serializing_if = "Option::is_none")]
1542    pub suppress_bottom_spacing: Option<bool>,
1543    #[serde(skip_serializing_if = "Option::is_none")]
1544    pub compatibility_mode: Option<i64>,
1545    #[serde(
1546        rename = "enableOpenTypeFeatures",
1547        skip_serializing_if = "Option::is_none"
1548    )]
1549    pub enable_open_type_features: Option<bool>,
1550    #[serde(skip_serializing_if = "Option::is_none")]
1551    pub suppress_spacing_before_after_page_break: Option<bool>,
1552    #[serde(skip_serializing_if = "Option::is_none")]
1553    pub use_printer_metrics: Option<bool>,
1554    #[serde(skip_serializing_if = "Option::is_none")]
1555    pub use_fixed_html_paragraph_spacing: Option<bool>,
1556    #[serde(skip_serializing_if = "Option::is_none")]
1557    pub do_not_break_wrapped_tables: Option<bool>,
1558    #[serde(skip_serializing_if = "Option::is_none")]
1559    pub do_not_break_constrained_forced_table: Option<bool>,
1560    #[serde(skip_serializing_if = "Option::is_none")]
1561    pub even_and_odd_headers: Option<bool>,
1562}
1563
1564/// Mirrors TypeScript `DocModel.metadata`.
1565#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1566#[serde(rename_all = "camelCase")]
1567pub struct DocModelMetadata {
1568    #[serde(default, skip_serializing_if = "Option::is_none")]
1569    pub document_id: Option<String>,
1570    #[serde(default, skip_serializing_if = "Option::is_none")]
1571    pub source_part_names: Option<std::collections::BTreeMap<String, String>>,
1572    pub source_parts: i64,
1573    pub warnings: Vec<String>,
1574    #[serde(skip_serializing_if = "Option::is_none")]
1575    pub document_page_count: Option<i64>,
1576    #[serde(skip_serializing_if = "Option::is_none")]
1577    pub document_open_tag: Option<String>,
1578    #[serde(skip_serializing_if = "Option::is_none")]
1579    pub document_background_color: Option<String>,
1580    #[serde(skip_serializing_if = "Option::is_none")]
1581    pub section_properties_xml: Option<String>,
1582    #[serde(skip_serializing_if = "Option::is_none")]
1583    pub sections: Option<Vec<DocumentSection>>,
1584    pub header_sections: Vec<HeaderSection>,
1585    pub footer_sections: Vec<FooterSection>,
1586    pub paragraph_styles: Vec<ParagraphStyleDefinition>,
1587    #[serde(skip_serializing_if = "Option::is_none")]
1588    pub default_paragraph_style_id: Option<String>,
1589    #[serde(skip_serializing_if = "Option::is_none")]
1590    pub default_tab_stop_twips: Option<i64>,
1591    #[serde(skip_serializing_if = "Option::is_none")]
1592    pub document_settings_imported: Option<bool>,
1593    #[serde(skip_serializing_if = "Option::is_none")]
1594    pub line_spacing_compatibility_imported: Option<bool>,
1595    #[serde(skip_serializing_if = "Option::is_none")]
1596    pub numbering_definitions: Option<NumberingDefinitionSet>,
1597    #[serde(skip_serializing_if = "Option::is_none")]
1598    pub compatibility: Option<DocumentCompatibilitySettings>,
1599    #[serde(skip_serializing_if = "Option::is_none")]
1600    pub footnotes: Option<Vec<DocumentNoteDefinition>>,
1601    #[serde(skip_serializing_if = "Option::is_none")]
1602    pub endnotes: Option<Vec<DocumentNoteDefinition>>,
1603    #[serde(skip_serializing_if = "Option::is_none")]
1604    pub comments: Option<Vec<DocumentCommentDefinition>>,
1605}
1606
1607/// Mirrors TypeScript `DocModel`.
1608#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1609#[serde(rename_all = "camelCase")]
1610pub struct DocModel {
1611    pub nodes: Vec<DocNode>,
1612    pub metadata: DocModelMetadata,
1613}
1614
1615#[cfg(test)]
1616mod deserialize_tests {
1617    use super::{DocModel, DocNode, HeadingLevel, ParagraphAlignment};
1618
1619    #[test]
1620    fn deserializes_minimal_js_doc_model() {
1621        let json = r#"{"nodes":[{"type":"paragraph","children":[{"type":"text","text":"Hi"}]}],"metadata":{"sourceParts":1,"warnings":[],"headerSections":[],"footerSections":[],"paragraphStyles":[]}}"#;
1622        let model: DocModel = serde_json::from_str(json).expect("should deserialize");
1623        assert_eq!(model.nodes.len(), 1);
1624    }
1625
1626    #[test]
1627    fn deserializes_heading_level_two() {
1628        let json = r#"{"type":"paragraph","style":{"headingLevel":2},"children":[{"type":"text","text":"Added"}]}"#;
1629        let node: DocNode = serde_json::from_str(json).expect("heading level 2");
1630        let DocNode::Paragraph(paragraph) = node else {
1631            panic!("expected paragraph");
1632        };
1633        assert_eq!(
1634            paragraph.style.and_then(|style| style.heading_level),
1635            Some(HeadingLevel::Two)
1636        );
1637    }
1638
1639    #[test]
1640    fn deserializes_align_center() {
1641        let json = r#"{"type":"paragraph","style":{"align":"center"},"children":[{"type":"text","text":"Added"}]}"#;
1642        let node: DocNode = serde_json::from_str(json).expect("align center");
1643        let DocNode::Paragraph(paragraph) = node else {
1644            panic!("expected paragraph");
1645        };
1646        assert_eq!(
1647            paragraph.style.and_then(|style| style.align),
1648            Some(ParagraphAlignment::Center)
1649        );
1650    }
1651}
1652
1653/// Fully resolved formatting shared until a caller edits its value.
1654#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
1655#[serde(transparent)]
1656pub struct SharedParagraphStyle(std::sync::Arc<ParagraphStyle>);
1657
1658impl SharedParagraphStyle {
1659    pub fn into_owned(self) -> ParagraphStyle {
1660        std::sync::Arc::unwrap_or_clone(self.0)
1661    }
1662}
1663
1664impl From<ParagraphStyle> for SharedParagraphStyle {
1665    fn from(style: ParagraphStyle) -> Self {
1666        Self(std::sync::Arc::new(style))
1667    }
1668}
1669
1670impl std::ops::Deref for SharedParagraphStyle {
1671    type Target = ParagraphStyle;
1672
1673    fn deref(&self) -> &ParagraphStyle {
1674        &self.0
1675    }
1676}
1677
1678impl std::ops::DerefMut for SharedParagraphStyle {
1679    fn deref_mut(&mut self) -> &mut ParagraphStyle {
1680        std::sync::Arc::make_mut(&mut self.0)
1681    }
1682}