entelix_persistence/schema_version.rs
1//! Persisted-payload schema version stamps.
2//!
3//! Every JSON blob written through this crate carries a
4//! [`SessionSchemaVersion`] tag. On read, payloads with versions
5//! outside the build's accepted range surface as
6//! [`crate::PersistenceError::SchemaVersionMismatch`]. Silent
7//! degradation is forbidden — a future-version payload is rejected
8//! hard so a downgrade never corrupts state by writing back a
9//! lower-version blob.
10
11use serde::{Deserialize, Serialize};
12
13use crate::error::{PersistenceError, PersistenceResult};
14
15/// Highest schema version this build can read and write.
16pub const CURRENT_VERSION: u32 = 1;
17
18/// Lowest schema version this build still understands. When
19/// migrations land, bump this only after the migration ladder writes
20/// every prior payload to the new shape.
21pub const MIN_SUPPORTED_VERSION: u32 = 1;
22
23/// Schema version tag for persisted payloads.
24#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
25#[serde(transparent)]
26pub struct SessionSchemaVersion(pub u32);
27
28impl SessionSchemaVersion {
29 /// The version this build emits.
30 pub const CURRENT: Self = Self(CURRENT_VERSION);
31
32 /// Validate that `payload` is in `[MIN_SUPPORTED_VERSION,
33 /// CURRENT_VERSION]`. Errors are loud — never silently downgrade.
34 pub fn validate(self) -> PersistenceResult<()> {
35 if self.0 < MIN_SUPPORTED_VERSION || self.0 > CURRENT_VERSION {
36 return Err(PersistenceError::SchemaVersionMismatch {
37 payload: self.0,
38 min: MIN_SUPPORTED_VERSION,
39 current: CURRENT_VERSION,
40 });
41 }
42 Ok(())
43 }
44
45 /// Raw integer.
46 pub const fn raw(self) -> u32 {
47 self.0
48 }
49}
50
51impl Default for SessionSchemaVersion {
52 fn default() -> Self {
53 Self::CURRENT
54 }
55}