Skip to main content

easydoc_writer/builder/
doc_builder.rs

1//! 文档构建器 -- 构建 DOCX 文档的主入口。
2//!
3//! 对应 Java: `com.alibaba.excel.write.ExcelBuilder`
4
5use std::path::PathBuf;
6
7use easydoc_core::Result;
8use easydoc_core::metadata::DocumentMeta;
9use easydoc_core::types::HeadingLevel;
10
11use crate::executor::write_executor::DocWriteExecutor;
12use crate::{DocImage, Paragraph, Table};
13
14/// 构建完整 DOCX 文档的 Fluent 构建器。
15///
16/// 通过门面 `EasyDoc::document()` 方法创建。
17///
18/// 对应 Java: `com.alibaba.excel.write.ExcelBuilderImpl`
19///
20/// # 示例
21///
22/// ```ignore
23/// EasyDoc::document("report.docx")
24///     .title("Report")
25///     .add_heading("Section 1", HeadingLevel::H1)
26///     .add_paragraph(Paragraph::new().add_text("Content..."))
27///     .add_table(Table::from_data(&rows))
28///     .build()?
29///     .save()?;
30/// ```
31pub struct DocBuilder {
32    path: PathBuf,
33    meta: DocumentMeta,
34    elements: Vec<DocumentElement>,
35}
36
37/// A single element in the document — paragraph, table, image, etc.
38pub(crate) enum DocumentElement {
39    Heading { text: String, level: HeadingLevel },
40    Paragraph(Paragraph),
41    Table(Table),
42    Image(DocImage),
43    PageBreak,
44}
45
46impl DocBuilder {
47    /// Creates a new document builder targeting the given output path.
48    #[must_use]
49    pub fn new(path: impl Into<PathBuf>) -> Self {
50        Self {
51            path: path.into(),
52            meta: DocumentMeta::default(),
53            elements: Vec::new(),
54        }
55    }
56
57    /// Sets the document title.
58    #[must_use]
59    pub fn title(mut self, title: impl Into<String>) -> Self {
60        self.meta = self.meta.title(title);
61        self
62    }
63
64    /// Sets the document author.
65    #[must_use]
66    pub fn author(mut self, author: impl Into<String>) -> Self {
67        self.meta = self.meta.author(author);
68        self
69    }
70
71    /// Adds a heading paragraph.
72    #[must_use]
73    pub fn add_heading(mut self, text: impl Into<String>, level: HeadingLevel) -> Self {
74        self.elements.push(DocumentElement::Heading {
75            text: text.into(),
76            level,
77        });
78        self
79    }
80
81    /// Adds a paragraph.
82    #[must_use]
83    pub fn add_paragraph(mut self, paragraph: Paragraph) -> Self {
84        self.elements.push(DocumentElement::Paragraph(paragraph));
85        self
86    }
87
88    /// Adds a table.
89    #[must_use]
90    pub fn add_table(mut self, table: Table) -> Self {
91        self.elements.push(DocumentElement::Table(table));
92        self
93    }
94
95    /// Adds an image.
96    #[must_use]
97    pub fn add_image(mut self, image: DocImage) -> Self {
98        self.elements.push(DocumentElement::Image(image));
99        self
100    }
101
102    /// Adds a page break.
103    #[must_use]
104    pub fn add_page_break(mut self) -> Self {
105        self.elements.push(DocumentElement::PageBreak);
106        self
107    }
108
109    /// Finalises the builder and returns a [`DocWriteExecutor`] ready to save.
110    ///
111    /// # Errors
112    ///
113    /// Returns an error if the document cannot be assembled.
114    pub fn build(self) -> Result<DocWriteExecutor> {
115        DocWriteExecutor::new(self.path, self.meta, self.elements)
116    }
117
118    /// Builds and immediately saves the document to disk.
119    ///
120    /// # Errors
121    ///
122    /// Returns an error if the document cannot be written.
123    pub fn save(self) -> Result<()> {
124        self.build()?.save()
125    }
126
127    /// Builds and writes the document to a generic writer implementing `Write + Seek`.
128    ///
129    /// Corresponds to Hutool's `Word07Writer.flush(OutputStream)` pattern.
130    /// Useful for writing to memory buffers, HTTP responses, etc.
131    ///
132    /// # Errors
133    ///
134    /// Returns an I/O or ZIP error.
135    pub fn save_to_writer<W: std::io::Write + std::io::Seek>(self, writer: W) -> Result<()> {
136        self.build()?.save_to_writer(writer)
137    }
138
139    /// Builds and returns the document as a `Vec<u8>`.
140    ///
141    /// Useful for in-memory generation without touching the filesystem.
142    ///
143    /// # Errors
144    ///
145    /// Returns a ZIP error if packaging fails.
146    pub fn save_to_bytes(self) -> Result<Vec<u8>> {
147        self.build()?.save_to_bytes()
148    }
149}