mf_file/zipdoc/
writer.rs

1use std::io::{self, Write, Seek};
2use zip::{ZipWriter, write::SimpleFileOptions, CompressionMethod};
3
4// 基于 ZIP 的文档写入器(docx 风格容器)
5pub struct ZipDocumentWriter<W: Write + Seek> {
6    pub(crate) zip: ZipWriter<W>,
7    pub(crate) manifest: serde_json::Value,
8}
9
10impl<W: Write + Seek> ZipDocumentWriter<W> {
11    // 创建写入器
12    pub fn new(w: W) -> io::Result<Self> {
13        let zip = ZipWriter::new(w);
14        let manifest = serde_json::json!({ "version": 1, "entries": [] });
15        Ok(Self { zip, manifest })
16    }
17    // 读取当前 manifest 的不可变引用
18    pub fn manifest(&self) -> &serde_json::Value {
19        &self.manifest
20    }
21    // 读取当前 manifest 的可变引用(可自由添加自定义字段)
22    pub fn manifest_mut(&mut self) -> &mut serde_json::Value {
23        &mut self.manifest
24    }
25    // 替换 manifest(链式调用)
26    pub fn set_manifest(
27        &mut self,
28        manifest: serde_json::Value,
29    ) -> &mut Self {
30        self.manifest = manifest;
31        self
32    }
33    // 写入 JSON 文件(deflate 压缩)
34    pub fn add_json(
35        &mut self,
36        name: &str,
37        value: &serde_json::Value,
38    ) -> io::Result<()> {
39        let opts = SimpleFileOptions::default()
40            .compression_method(CompressionMethod::Deflated);
41        self.zip.start_file(name, opts)?;
42        let data = serde_json::to_vec(value).map_err(io::Error::other)?;
43        // 记录到 manifest.entries(若存在且为数组)
44        if let Some(entries) =
45            self.manifest.get_mut("entries").and_then(|v| v.as_array_mut())
46        {
47            entries.push(serde_json::json!({
48                "name": name,
49                "kind": "json",
50                "logical_len": data.len(),
51                "compression": "deflate"
52            }));
53        }
54        self.zip.write_all(&data)
55    }
56    // 写入原样存储的条目(不压缩)
57    pub fn add_stored(
58        &mut self,
59        name: &str,
60        bytes: &[u8],
61    ) -> io::Result<()> {
62        let opts = SimpleFileOptions::default()
63            .compression_method(CompressionMethod::Stored);
64        self.zip.start_file(name, opts)?;
65        if let Some(entries) =
66            self.manifest.get_mut("entries").and_then(|v| v.as_array_mut())
67        {
68            entries.push(serde_json::json!({
69                "name": name,
70                "kind": "binary",
71                "logical_len": bytes.len(),
72                "compression": "stored"
73            }));
74        }
75        self.zip.write_all(bytes)
76    }
77
78    // 添加插件状态目录和文件(二进制存储)
79    pub fn add_plugin_state(
80        &mut self,
81        plugin_name: &str,
82        state_data: &[u8],
83    ) -> io::Result<()> {
84        let plugin_file_path = format!("plugins/{plugin_name}");
85        let opts = SimpleFileOptions::default()
86            .compression_method(CompressionMethod::Deflated);
87        self.zip.start_file(&plugin_file_path, opts)?;
88
89        if let Some(entries) =
90            self.manifest.get_mut("entries").and_then(|v| v.as_array_mut())
91        {
92            entries.push(serde_json::json!({
93                "name": plugin_file_path,
94                "kind": "plugin_state",
95                "plugin": plugin_name,
96                "logical_len": state_data.len(),
97                "compression": "deflate"
98            }));
99        }
100        self.zip.write_all(state_data)
101    }
102
103    // 批量添加插件状态
104    pub fn add_plugin_states<I>(
105        &mut self,
106        plugin_states: I,
107    ) -> io::Result<()>
108    where
109        I: IntoIterator<Item = (String, Vec<u8>)>,
110    {
111        for (plugin_name, state_data) in plugin_states {
112            self.add_plugin_state(&plugin_name, &state_data)?;
113        }
114        Ok(())
115    }
116    // 写入 deflate 压缩条目
117    pub fn add_deflated(
118        &mut self,
119        name: &str,
120        bytes: &[u8],
121    ) -> io::Result<()> {
122        let opts = SimpleFileOptions::default()
123            .compression_method(CompressionMethod::Deflated);
124        self.zip.start_file(name, opts)?;
125        if let Some(entries) =
126            self.manifest.get_mut("entries").and_then(|v| v.as_array_mut())
127        {
128            entries.push(serde_json::json!({
129                "name": name,
130                "kind": "binary",
131                "logical_len": bytes.len(),
132                "compression": "deflate"
133            }));
134        }
135        self.zip.write_all(bytes)
136    }
137    // 完成写入,附带 manifest.json
138    pub fn finalize(mut self) -> io::Result<W> {
139        let opts = SimpleFileOptions::default()
140            .compression_method(CompressionMethod::Deflated);
141        self.zip.start_file("manifest.json", opts)?;
142        let data =
143            serde_json::to_vec(&self.manifest).map_err(io::Error::other)?;
144        self.zip.write_all(&data)?;
145        self.zip.finish().map_err(io::Error::other)
146    }
147}
148
149#[cfg(test)]
150mod tests {
151    use super::*;
152    use std::io::Cursor;
153
154    #[test]
155    fn test_plugin_state_export() {
156        let buffer = Vec::new();
157        let cursor = Cursor::new(buffer);
158        let mut writer = ZipDocumentWriter::new(cursor).unwrap();
159
160        // 添加插件状态
161        writer.add_plugin_state("test_plugin", b"test state data").unwrap();
162        writer
163            .add_plugin_state("another_plugin", b"another state data")
164            .unwrap();
165
166        let result = writer.finalize().unwrap();
167        let final_data = result.into_inner();
168
169        // 验证数据已写入
170        assert!(!final_data.is_empty());
171
172        // 验证 manifest 包含插件条目
173        let cursor = Cursor::new(&final_data);
174        let mut reader = crate::zipdoc::ZipDocumentReader::new(cursor).unwrap();
175
176        let plugins = reader.list_plugins().unwrap();
177        assert_eq!(plugins.len(), 2);
178        assert!(plugins.contains(&"test_plugin".to_string()));
179        assert!(plugins.contains(&"another_plugin".to_string()));
180
181        // 验证插件状态可以读取
182        let test_state =
183            reader.read_plugin_state("test_plugin").unwrap().unwrap();
184        assert_eq!(test_state, b"test state data");
185    }
186
187    #[test]
188    fn test_batch_plugin_states() {
189        let buffer = Vec::new();
190        let cursor = Cursor::new(buffer);
191        let mut writer = ZipDocumentWriter::new(cursor).unwrap();
192
193        let plugin_states = vec![
194            ("plugin1".to_string(), b"state1".to_vec()),
195            ("plugin2".to_string(), b"state2".to_vec()),
196            ("plugin3".to_string(), b"state3".to_vec()),
197        ];
198
199        writer.add_plugin_states(plugin_states.clone()).unwrap();
200        let result = writer.finalize().unwrap();
201        let final_data = result.into_inner();
202
203        let cursor = Cursor::new(&final_data);
204        let mut reader = crate::zipdoc::ZipDocumentReader::new(cursor).unwrap();
205
206        let all_states = reader.read_all_plugin_states().unwrap();
207        assert_eq!(all_states.len(), 3);
208
209        for (name, expected_data) in plugin_states {
210            let actual_data = all_states.get(&name).unwrap();
211            assert_eq!(actual_data, &expected_data);
212        }
213    }
214}