Skip to main content

icechunk_format/serializers/
mod.rs

1//! Flatbuffer serialization for Icechunk metadata.
2//!
3//! How serializers work:
4//!
5//! - Main goal is to make sure newer version of Icechunk can read metadata files created using
6//!   older versions. In this way, a repository can evolve during its life. As users upgrade their
7//!   Icechunk versions they don't need to migrate their data.
8//! - Of course we may choose to limit backwards compatibility after certain number of versions or
9//!   a time limit.
10//! - Performance is critical, so we cannot copy much data around during the process of
11//!   serialization/deserialization
12//! - For serialization:
13//!     - We define a new `XSerializer` for each metadata file type `X`. Example: `SnapshotSerializer`.
14//!     - This type implement [`serde::Serialize`]
15//!     - This type holds only references to the same fields as `X`
16//!     - This type implements `From<&X>` (notice by reference) Example:
17//!       ```ignore
18//!       impl<'a> From<&'a Snapshot> for SnapshotSerializer<'a> {
19//!       ...
20//!       }
21//!       ```
22//!     - Because the serializer only holds references it's essentially free to call
23//!       `snapshot.into()` to get one.
24//!     - Then this object is serialized using serde.
25//! - For deserialization:
26//!     - We define a new `XDeserializer` for each metadata file type `X`. Example: `SnapshotDeserializer`.
27//!     - This type implement [`serde::Deserialize`]
28//!     - This type holds the same fields as `X` by value
29//!     - `X` implements `From<XDeserializer>` (notice by value). Example:
30//!       ```ignore
31//!        impl From<SnapshotDeserializer> for Snapshot {
32//!        ...
33//!        }
34//!       ```
35//!     - Because the deserializer can be destructed and `X` implements `From`,  it's essentially free to call
36//!       obtain the original type `X`
37//!     - Then this new type `XDeserializer` is deserialized using serde and converted with `into`.
38//!
39//! - `serializers.current.rs` holds all the serializers and deserializers for the current version
40//!   of the spec
41//! - `serializers.version_foo.rs` holds all the serializers and deserializers for version foo of
42//!   the spec
43//! - The `serializers` module root has functions `serialize_X` and `deserialize_X` that take a
44//!   spec version number and use the right (de)-serializer to do the job.
45use std::io::Write;
46
47use icechunk_types::ICResultExt as _;
48
49use crate::{
50    IcechunkFormatError, IcechunkFormatErrorKind, format_constants::SpecVersionBin,
51    manifest::Manifest, repo_info::RepoInfo, snapshot::Snapshot,
52    transaction_log::TransactionLog,
53};
54
55pub fn serialize_snapshot(
56    snapshot: &Snapshot,
57    version: SpecVersionBin,
58    write: &mut impl Write,
59) -> Result<(), std::io::Error> {
60    match version {
61        SpecVersionBin::V1 | SpecVersionBin::V2 => write.write_all(snapshot.bytes()),
62    }
63}
64
65pub fn serialize_manifest(
66    manifest: &Manifest,
67    version: SpecVersionBin,
68    write: &mut impl Write,
69) -> Result<(), std::io::Error> {
70    match version {
71        SpecVersionBin::V1 | SpecVersionBin::V2 => write.write_all(manifest.bytes()),
72    }
73}
74
75pub fn serialize_transaction_log(
76    transaction_log: &TransactionLog,
77    version: SpecVersionBin,
78    write: &mut impl Write,
79) -> Result<(), std::io::Error> {
80    match version {
81        SpecVersionBin::V1 | SpecVersionBin::V2 => {
82            write.write_all(transaction_log.bytes())
83        }
84    }
85}
86
87pub fn serialize_repo_info(
88    info: &RepoInfo,
89    version: SpecVersionBin,
90    write: &mut impl Write,
91) -> Result<(), std::io::Error> {
92    match version {
93        SpecVersionBin::V2 => write.write_all(info.bytes()),
94        SpecVersionBin::V1 => Err(std::io::Error::new(
95            std::io::ErrorKind::Unsupported,
96            "Trying to write to an old Icechunk format version. Aborting.",
97        )),
98    }
99}
100
101pub fn deserialize_snapshot(
102    version: SpecVersionBin,
103    buffer: Vec<u8>,
104) -> Result<Snapshot, IcechunkFormatError> {
105    match version {
106        SpecVersionBin::V1 | SpecVersionBin::V2 => Snapshot::from_buffer(version, buffer),
107    }
108}
109
110pub fn deserialize_manifest(
111    version: SpecVersionBin,
112    buffer: Vec<u8>,
113) -> Result<Manifest, IcechunkFormatError> {
114    match version {
115        SpecVersionBin::V1 | SpecVersionBin::V2 => Manifest::from_buffer(buffer),
116    }
117}
118
119pub fn deserialize_transaction_log(
120    version: SpecVersionBin,
121    buffer: Vec<u8>,
122) -> Result<TransactionLog, IcechunkFormatError> {
123    match version {
124        SpecVersionBin::V1 | SpecVersionBin::V2 => TransactionLog::from_buffer(buffer),
125    }
126}
127
128pub fn deserialize_repo_info(
129    version: SpecVersionBin,
130    buffer: Vec<u8>,
131) -> Result<RepoInfo, IcechunkFormatError> {
132    match version {
133        SpecVersionBin::V2 => RepoInfo::from_buffer(buffer),
134        SpecVersionBin::V1 => {
135            Err(IcechunkFormatErrorKind::UnsupportedOperationForVersion {
136                version: SpecVersionBin::V1 as u8,
137            })
138            .capture()
139        }
140    }
141}