Skip to main content

ic_memory/
schema.rs

1use serde::{Deserialize, Serialize};
2
3///
4/// SchemaMetadata
5///
6/// Optional diagnostic metadata for an in-place store schema.
7#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
8pub struct SchemaMetadata {
9    /// Optional in-place schema version.
10    pub schema_version: Option<u32>,
11    /// Optional opaque schema fingerprint.
12    pub schema_fingerprint: Option<String>,
13}
14
15impl SchemaMetadata {
16    /// Construct schema metadata after validating the persisted encoding bounds.
17    pub fn new(
18        schema_version: Option<u32>,
19        schema_fingerprint: Option<String>,
20    ) -> Result<Self, SchemaMetadataError> {
21        let metadata = Self {
22            schema_version,
23            schema_fingerprint,
24        };
25        metadata.validate()?;
26        Ok(metadata)
27    }
28
29    /// Validate schema metadata encoding rules.
30    pub fn validate(&self) -> Result<(), SchemaMetadataError> {
31        if self.schema_version == Some(0) {
32            return Err(SchemaMetadataError::InvalidVersion);
33        }
34
35        let Some(fingerprint) = &self.schema_fingerprint else {
36            return Ok(());
37        };
38
39        if fingerprint.is_empty() {
40            return Err(SchemaMetadataError::EmptyFingerprint);
41        }
42        if fingerprint.len() > 256 {
43            return Err(SchemaMetadataError::FingerprintTooLong);
44        }
45        if !fingerprint.is_ascii() {
46            return Err(SchemaMetadataError::NonAsciiFingerprint);
47        }
48        if fingerprint.bytes().any(|byte| byte.is_ascii_control()) {
49            return Err(SchemaMetadataError::ControlCharacterFingerprint);
50        }
51
52        Ok(())
53    }
54}
55
56///
57/// SchemaMetadataError
58///
59/// Schema metadata validation failure.
60#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
61pub enum SchemaMetadataError {
62    /// Schema version zero is reserved for absence.
63    #[error("schema_version must be greater than zero when present")]
64    InvalidVersion,
65    /// Present fingerprints must be non-empty.
66    #[error("schema_fingerprint must not be empty when present")]
67    EmptyFingerprint,
68    /// Fingerprints must stay bounded for durable ledger storage.
69    #[error("schema_fingerprint must be at most 256 bytes")]
70    FingerprintTooLong,
71    /// Fingerprints must not require Unicode normalization.
72    #[error("schema_fingerprint must be ASCII")]
73    NonAsciiFingerprint,
74    /// Fingerprints must be printable metadata.
75    #[error("schema_fingerprint must not contain ASCII control characters")]
76    ControlCharacterFingerprint,
77}