mf_file/zipdoc/formats/
strategy.rs

1use std::io::{self, Read, Seek, Write};
2use std::path::Path;
3use serde::{Serialize, de::DeserializeOwned};
4
5use crate::zipdoc::{ZipDocumentReader, ZipDocumentWriter};
6use crate::zipdoc::snapshot::SnapshotShardMeta;
7
8use super::{json, cbor, msgpack};
9
10#[derive(Debug, Clone, Copy)]
11pub enum SnapshotFormat {
12    Json,
13    Cbor,
14    MsgPack,
15}
16
17impl SnapshotFormat {
18    pub fn write_shards<W, F, T>(
19        &self,
20        zw: &mut ZipDocumentWriter<W>,
21        meta: &SnapshotShardMeta,
22        get_shard_value: F,
23        zstd_level: i32,
24    ) -> io::Result<()>
25    where
26        W: Write + Seek,
27        F: FnMut(usize) -> io::Result<T>,
28        T: Serialize,
29    {
30        match self {
31            SnapshotFormat::Json => json::write_snapshot_shards_json(
32                zw,
33                meta,
34                get_shard_value,
35                zstd_level,
36            ),
37            SnapshotFormat::Cbor => cbor::write_snapshot_shards_cbor(
38                zw,
39                meta,
40                get_shard_value,
41                zstd_level,
42            ),
43            SnapshotFormat::MsgPack => msgpack::write_snapshot_shards_msgpack(
44                zw,
45                meta,
46                get_shard_value,
47                zstd_level,
48            ),
49        }
50    }
51
52    pub fn read_shards<R, T>(
53        &self,
54        zr: &mut ZipDocumentReader<R>,
55    ) -> io::Result<(SnapshotShardMeta, Vec<T>)>
56    where
57        R: Read + Seek,
58        T: DeserializeOwned,
59    {
60        match self {
61            SnapshotFormat::Json => {
62                json::read_and_decode_snapshot_shards_json(zr)
63            },
64            SnapshotFormat::Cbor => {
65                cbor::read_and_decode_snapshot_shards_cbor(zr)
66            },
67            SnapshotFormat::MsgPack => {
68                msgpack::read_and_decode_snapshot_shards_msgpack(zr)
69            },
70        }
71    }
72
73    pub fn for_each_shard<R, T, F>(
74        &self,
75        zr: &mut ZipDocumentReader<R>,
76        on_shard: F,
77    ) -> io::Result<SnapshotShardMeta>
78    where
79        R: Read + Seek,
80        T: DeserializeOwned,
81        F: FnMut(usize, T) -> io::Result<()>,
82    {
83        match self {
84            SnapshotFormat::Json => {
85                json::for_each_snapshot_shard_json(zr, on_shard)
86            },
87            SnapshotFormat::Cbor => {
88                cbor::for_each_snapshot_shard_cbor(zr, on_shard)
89            },
90            SnapshotFormat::MsgPack => {
91                msgpack::for_each_snapshot_shard_msgpack(zr, on_shard)
92            },
93        }
94    }
95
96    pub fn write_parent_map<W, T>(
97        &self,
98        zw: &mut ZipDocumentWriter<W>,
99        parent_map: &T,
100        zstd_level: i32,
101    ) -> io::Result<()>
102    where
103        W: Write + Seek,
104        T: Serialize,
105    {
106        match self {
107            SnapshotFormat::Json => {
108                json::write_parent_map_json(zw, parent_map, zstd_level)
109            },
110            SnapshotFormat::Cbor => {
111                cbor::write_parent_map_cbor(zw, parent_map, zstd_level)
112            },
113            SnapshotFormat::MsgPack => {
114                msgpack::write_parent_map_msgpack(zw, parent_map, zstd_level)
115            },
116        }
117    }
118
119    pub fn read_parent_map<R, T>(
120        &self,
121        zr: &mut ZipDocumentReader<R>,
122    ) -> io::Result<T>
123    where
124        R: Read + Seek,
125        T: DeserializeOwned,
126    {
127        match self {
128            SnapshotFormat::Json => json::read_parent_map_json(zr),
129            SnapshotFormat::Cbor => cbor::read_parent_map_cbor(zr),
130            SnapshotFormat::MsgPack => msgpack::read_parent_map_msgpack(zr),
131        }
132    }
133}
134
135impl SnapshotFormat {
136    pub fn as_str(&self) -> &'static str {
137        match self {
138            SnapshotFormat::Json => "json",
139            SnapshotFormat::Cbor => "cbor",
140            SnapshotFormat::MsgPack => "msgpack",
141        }
142    }
143    #[allow(clippy::should_implement_trait)]
144    pub fn from_str(s: &str) -> Option<Self> {
145        match s.to_ascii_lowercase().as_str() {
146            "json" => Some(SnapshotFormat::Json),
147            "cbor" => Some(SnapshotFormat::Cbor),
148            "msgpack" | "rmp" | "msg" => Some(SnapshotFormat::MsgPack),
149            _ => None,
150        }
151    }
152    pub fn from_extension<P: AsRef<Path>>(path: P) -> Option<Self> {
153        match path
154            .as_ref()
155            .extension()
156            .and_then(|e| e.to_str())
157            .map(|s| s.to_ascii_lowercase())
158        {
159            Some(ext) if ext == "json" => Some(SnapshotFormat::Json),
160            Some(ext) if ext == "cbor" || ext == "cbr" => {
161                Some(SnapshotFormat::Cbor)
162            },
163            Some(ext) if ext == "msgpack" || ext == "rmp" || ext == "msg" => {
164                Some(SnapshotFormat::MsgPack)
165            },
166            _ => None,
167        }
168    }
169}
170
171// 高层封装:根据策略导出 ZIP(meta.json + schema.xml + 分片 + 可选 parent_map + 插件状态)
172#[allow(clippy::too_many_arguments)]
173pub fn export_zip_with_format<P, F, T, PM>(
174    path: P,
175    meta_json: &serde_json::Value,
176    schema_xml: &[u8],
177    shard_meta: &SnapshotShardMeta,
178    get_shard_value: F,
179    parent_map: Option<&PM>,
180    plugin_states: Option<std::collections::HashMap<String, Vec<u8>>>,
181    zstd_level: i32,
182    format: SnapshotFormat,
183) -> io::Result<()>
184where
185    P: AsRef<Path>,
186    F: FnMut(usize) -> io::Result<T>,
187    T: Serialize,
188    PM: Serialize,
189{
190    let file = std::fs::File::create(path)?;
191    let mut zw = ZipDocumentWriter::new(file)?;
192    zw.add_json("meta.json", meta_json)?;
193    zw.add_deflated("schema.xml", schema_xml)?;
194    format.write_shards(&mut zw, shard_meta, get_shard_value, zstd_level)?;
195    if let Some(pm) = parent_map {
196        format.write_parent_map(&mut zw, pm, zstd_level)?;
197    }
198    if let Some(states) = plugin_states {
199        zw.add_plugin_states(states)?;
200    }
201    let _ = zw.finalize()?;
202    Ok(())
203}
204
205// 高层封装:根据策略导入 ZIP(返回 meta.json, schema.xml, 分片, 可选 parent_map, 插件状态)
206#[allow(clippy::type_complexity)]
207pub fn import_zip_with_format<P, T, PM>(
208    path: P,
209    format: SnapshotFormat,
210    read_parent_map: bool,
211    read_plugin_states: bool,
212) -> io::Result<(
213    serde_json::Value,
214    Vec<u8>,
215    SnapshotShardMeta,
216    Vec<T>,
217    Option<PM>,
218    Option<std::collections::HashMap<String, Vec<u8>>>,
219)>
220where
221    P: AsRef<Path>,
222    T: DeserializeOwned,
223    PM: DeserializeOwned,
224{
225    let file = std::fs::File::open(path)?;
226    let mut zr = ZipDocumentReader::new(file)?;
227    let meta_json = zr.read_all("meta.json")?;
228    let meta_val: serde_json::Value =
229        serde_json::from_slice(&meta_json).map_err(io::Error::other)?;
230    let schema_xml = zr.read_all("schema.xml")?;
231    let (shard_meta, decoded) = format.read_shards::<_, T>(&mut zr)?;
232    let parent_map = if read_parent_map {
233        Some(format.read_parent_map::<_, PM>(&mut zr)?)
234    } else {
235        None
236    };
237    let plugin_states = if read_plugin_states {
238        Some(zr.read_all_plugin_states()?)
239    } else {
240        None
241    };
242    Ok((meta_val, schema_xml, shard_meta, decoded, parent_map, plugin_states))
243}
244
245// 便利函数:只导出插件状态(用于纯插件状态备份)
246pub fn export_plugin_states_only<P>(
247    path: P,
248    plugin_states: std::collections::HashMap<String, Vec<u8>>,
249    meta_json: Option<&serde_json::Value>,
250) -> io::Result<()>
251where
252    P: AsRef<Path>,
253{
254    let file = std::fs::File::create(path)?;
255    let mut zw = ZipDocumentWriter::new(file)?;
256
257    // 添加元数据(如果提供)
258    if let Some(meta) = meta_json {
259        zw.add_json("meta.json", meta)?;
260    } else {
261        // 默认元数据
262        let timestamp = {
263            #[cfg(feature = "chrono")]
264            {
265                chrono::Utc::now().to_rfc3339()
266            }
267            #[cfg(not(feature = "chrono"))]
268            {
269                std::time::SystemTime::now()
270                    .duration_since(std::time::UNIX_EPOCH)
271                    .unwrap_or_default()
272                    .as_secs()
273                    .to_string()
274            }
275        };
276
277        let default_meta = serde_json::json!({
278            "type": "plugin_states_backup",
279            "version": "1.0",
280            "plugin_count": plugin_states.len(),
281            "timestamp": timestamp
282        });
283        zw.add_json("meta.json", &default_meta)?;
284    }
285
286    // 添加插件状态
287    zw.add_plugin_states(plugin_states)?;
288    let _ = zw.finalize()?;
289    Ok(())
290}
291
292// 便利函数:只导入插件状态
293pub fn import_plugin_states_only<P>(
294    path: P
295) -> io::Result<(serde_json::Value, std::collections::HashMap<String, Vec<u8>>)>
296where
297    P: AsRef<Path>,
298{
299    let file = std::fs::File::open(path)?;
300    let mut zr = ZipDocumentReader::new(file)?;
301
302    let meta_json = zr.read_all("meta.json")?;
303    let meta_val: serde_json::Value =
304        serde_json::from_slice(&meta_json).map_err(io::Error::other)?;
305
306    let plugin_states = zr.read_all_plugin_states()?;
307    Ok((meta_val, plugin_states))
308}
309
310// 便利函数:检查ZIP是否包含插件状态
311pub fn has_plugin_states<P>(path: P) -> io::Result<bool>
312where
313    P: AsRef<Path>,
314{
315    let file = std::fs::File::open(path)?;
316    let mut zr = ZipDocumentReader::new(file)?;
317    let plugins = zr.list_plugins()?;
318    Ok(!plugins.is_empty())
319}
320
321// 便利函数:列出ZIP中的所有插件
322pub fn list_zip_plugins<P>(path: P) -> io::Result<Vec<String>>
323where
324    P: AsRef<Path>,
325{
326    let file = std::fs::File::open(path)?;
327    let mut zr = ZipDocumentReader::new(file)?;
328    zr.list_plugins()
329}