Skip to main content

weiss_core/db/
serialization.rs

1use std::fs;
2use std::path::Path;
3
4use anyhow::{Context, Result};
5
6use super::store::CardDb;
7
8const WSDB_MAGIC: &[u8; 4] = b"WSDB";
9const MAX_WSDB_BYTES: u64 = 128 * 1024 * 1024;
10/// Current wsdb schema version.
11pub const WSDB_SCHEMA_VERSION: u32 = 2;
12
13impl CardDb {
14    /// Load a WSDB v2 file from disk.
15    pub fn load<P: AsRef<Path>>(path: P) -> Result<Self> {
16        let path = path.as_ref();
17        let metadata =
18            fs::metadata(path).with_context(|| format!("Failed to stat card db {path:?}"))?;
19        if metadata.len() > MAX_WSDB_BYTES {
20            anyhow::bail!(
21                "Card db file {path:?} is {} bytes, exceeding maximum {MAX_WSDB_BYTES} bytes",
22                metadata.len()
23            );
24        }
25        let bytes = fs::read(path).with_context(|| format!("Failed to read card db {path:?}"))?;
26        Self::from_wsdb_bytes(&bytes)
27    }
28
29    /// Parse a WSDB v2 file from bytes.
30    pub fn from_wsdb_bytes(bytes: &[u8]) -> Result<Self> {
31        if bytes.len() as u64 > MAX_WSDB_BYTES {
32            anyhow::bail!(
33                "Card db byte buffer is {} bytes, exceeding maximum {MAX_WSDB_BYTES} bytes",
34                bytes.len()
35            );
36        }
37        if bytes.len() < 8 {
38            anyhow::bail!("Card db file too small");
39        }
40        if &bytes[0..4] != WSDB_MAGIC {
41            anyhow::bail!("Card db magic mismatch; expected WSDB header");
42        }
43        let version = u32::from_le_bytes(
44            bytes[4..8]
45                .try_into()
46                .map_err(|_| anyhow::anyhow!("Card db header missing version bytes"))?,
47        );
48        if version != WSDB_SCHEMA_VERSION {
49            anyhow::bail!(
50                "Unsupported card db schema version {version}, expected {WSDB_SCHEMA_VERSION}. \
51                 This build only loads WSDB v2 files; regenerate the card DB with the current \
52                 parser-v2 rule-pack pipeline."
53            );
54        }
55        let payload = &bytes[8..];
56        Self::from_postcard_payload(payload)
57    }
58
59    /// Parse the postcard payload (without WSDB header) into a `CardDb`.
60    pub fn from_postcard_payload(payload: &[u8]) -> Result<Self> {
61        let mut db: CardDb =
62            postcard::from_bytes(payload).context("Failed to decode card db payload")?;
63        db.build_index()?;
64        Ok(db)
65    }
66
67    /// Return the current WSDB schema version supported by this build.
68    pub fn schema_version() -> u32 {
69        WSDB_SCHEMA_VERSION
70    }
71
72    /// Serialize this database to a WSDB v2 byte buffer (including header).
73    pub fn to_bytes_with_header(&self) -> Result<Vec<u8>> {
74        let payload = postcard::to_stdvec(self)?;
75        let mut out = Vec::with_capacity(8 + payload.len());
76        out.extend_from_slice(WSDB_MAGIC);
77        out.extend_from_slice(&WSDB_SCHEMA_VERSION.to_le_bytes());
78        out.extend_from_slice(&payload);
79        Ok(out)
80    }
81}