Skip to main content

docx_rs/documents/elements/
abstract_numbering.rs

1use crate::documents::{BuildXML, Level};
2use crate::xml_builder::*;
3use std::io::Write;
4
5use serde::Serialize;
6
7#[derive(Debug, Clone, PartialEq, Serialize)]
8#[serde(rename_all = "camelCase")]
9pub struct AbstractNumbering {
10    pub id: usize,
11    pub style_link: Option<String>,
12    pub num_style_link: Option<String>,
13    pub levels: Vec<Level>,
14    #[serde(skip_serializing_if = "Option::is_none")]
15    pub multi_level_type: Option<String>,
16}
17
18impl AbstractNumbering {
19    pub fn new(id: usize) -> Self {
20        Self {
21            id,
22            style_link: None,
23            num_style_link: None,
24            levels: vec![],
25            multi_level_type: None,
26        }
27    }
28
29    pub fn add_level(mut self, level: Level) -> Self {
30        self.levels.push(level);
31        self
32    }
33
34    pub fn num_style_link(mut self, link: impl Into<String>) -> Self {
35        self.num_style_link = Some(link.into());
36        self
37    }
38
39    pub fn style_link(mut self, link: impl Into<String>) -> Self {
40        self.style_link = Some(link.into());
41        self
42    }
43}
44
45impl BuildXML for AbstractNumbering {
46    fn build_to<W: Write>(
47        &self,
48        stream: crate::xml::writer::EventWriter<W>,
49    ) -> crate::xml::writer::Result<crate::xml::writer::EventWriter<W>> {
50        let mut builder = XMLBuilder::from(stream).open_abstract_num(&self.id.to_string())?;
51
52        // 添加 multiLevelType 元素(如果存在)
53        if let Some(ref multi_level_type) = self.multi_level_type {
54            builder = builder.multi_level_type(multi_level_type)?;
55        }
56
57        builder.add_children(&self.levels)?.close()?.into_inner()
58    }
59}
60
61#[cfg(test)]
62mod tests {
63
64    use super::*;
65    #[cfg(test)]
66    use crate::documents::{Level, LevelJc, LevelText, NumberFormat, Start};
67    use pretty_assertions::assert_eq;
68    use std::str;
69
70    #[test]
71    fn test_numbering() {
72        let mut c = AbstractNumbering::new(0);
73        c = c.add_level(Level::new(
74            1,
75            Start::new(1),
76            NumberFormat::new("decimal"),
77            LevelText::new("%4."),
78            LevelJc::new("left"),
79        ));
80        let b = c.build();
81        assert_eq!(
82            str::from_utf8(&b).unwrap(),
83            r#"<w:abstractNum w:abstractNumId="0"><w:lvl w:ilvl="1"><w:start w:val="1" /><w:numFmt w:val="decimal" /><w:lvlText w:val="%4." /><w:lvlJc w:val="left" /><w:pPr><w:rPr /></w:pPr><w:rPr /></w:lvl></w:abstractNum>"#
84        );
85    }
86
87    #[test]
88    fn test_numbering_json() {
89        let mut c = AbstractNumbering::new(0);
90        c = c
91            .add_level(Level::new(
92                1,
93                Start::new(1),
94                NumberFormat::new("decimal"),
95                LevelText::new("%4."),
96                LevelJc::new("left"),
97            ))
98            .num_style_link("style1");
99        assert_eq!(
100            serde_json::to_string(&c).unwrap(),
101            r#"{"id":0,"styleLink":null,"numStyleLink":"style1","levels":[{"level":1,"start":1,"format":"decimal","text":"%4.","jc":"left","paragraphProperty":{"runProperty":{},"tabs":[]},"runProperty":{},"suffix":"tab","pstyle":null,"levelRestart":null}]}"#,
102        );
103    }
104}