Skip to main content

harmos_domain/model/storage/files/
codec.rs

1/* ***
2 *
3 * Harmos
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * ***
8 */
9
10/* === Modules === */
11
12/* === Re-Exports === */
13
14/* === Imports === */
15
16// Standard Library Imports
17use std::borrow::Cow;
18use std::num::NonZeroUsize;
19// Third Party Imports
20
21// Local Imports
22use super::{FileMetadata, FileReference};
23
24use crate::model::storage::StorageCursor;
25use crate::model::storage::position::codec::{decode_bounded, encode_bounded};
26use crate::schema::declaration::{Field, Kind};
27use crate::schema::{Cardinality, Described};
28use crate::{Error, MessageError, Result};
29
30/* === Constants === */
31
32/* === Macros === */
33
34crate::model::storage::codec::product!(super::FileSession {
35    history: crate::model::storage::codec::Nested<crate::model::storage::StoreHistory> => 1,
36    id: crate::model::storage::codec::Id => 2,
37    generation: crate::model::storage::codec::Unsigned => 3,
38});
39
40crate::model::storage::codec::product!(super::StagedFile {
41    history: crate::model::storage::codec::Nested<crate::model::storage::StoreHistory> => 1,
42    id: crate::model::storage::codec::Id => 2,
43});
44crate::model::storage::codec::product!(super::FileProgress {
45    length: crate::model::storage::codec::Unsigned => 1,
46    phase: Phase => 2,
47});
48
49/* === Type Aliases === */
50
51/* === Structs === */
52
53// Wire Values: Zero Producing, One Finished, Two Abandoned.
54struct Phase;
55
56/* === Enums === */
57
58/* === Traits === */
59
60/* === Implementations === */
61
62impl crate::model::storage::codec::Value for Phase {
63    const NAME: &'static str = concat!(module_path!(), "::", stringify!(Phase));
64    fn postcard_write(value: &Self::Native, bytes: &mut Vec<u8>) {
65        crate::__private::PostcardValue::write(
66            &match value {
67                super::FilePhase::Producing => 0u64,
68                super::FilePhase::Finished => 1u64,
69                super::FilePhase::Abandoned => 2u64,
70            },
71            bytes,
72        );
73    }
74
75    fn postcard_size(_: &Self::Native) -> usize {
76        1
77    }
78
79    fn postcard_memory(input: &mut crate::__private::PostcardReader<'_>) -> std::result::Result<usize, MessageError> {
80        Self::postcard_read(input)?;
81        Ok(0)
82    }
83
84    fn postcard_read(input: &mut crate::__private::PostcardReader<'_>) -> std::result::Result<Self::Native, MessageError> {
85        match input.value::<u64>()? {
86            0 => Ok(super::FilePhase::Producing),
87            1 => Ok(super::FilePhase::Finished),
88            2 => Ok(super::FilePhase::Abandoned),
89            _ => Err(MessageError::borrowed("invalid storage selector")),
90        }
91    }
92
93    type Native = super::FilePhase;
94    const KIND: Kind = Kind::U64;
95}
96
97impl FileMetadata {
98    pub fn encode_bounded(&self, max_bytes: NonZeroUsize) -> Result<Vec<u8>> {
99        encode_bounded(self, max_bytes)
100    }
101
102    pub fn decode_bounded(bytes: &[u8], max_bytes: NonZeroUsize) -> Result<Self> {
103        decode_bounded(bytes, max_bytes)
104    }
105}
106
107impl FileReference {
108    pub fn encode_bounded(&self, max_bytes: NonZeroUsize) -> Result<Vec<u8>> {
109        self.check_native().map_err(|_| Error::InvalidInput { field: "storage file codec", reason: "invalid file reference" })?;
110        encode_bounded(self, max_bytes)
111    }
112
113    pub fn decode_bounded(bytes: &[u8], max_bytes: NonZeroUsize) -> Result<Self> {
114        decode_bounded(bytes, max_bytes)
115    }
116
117    pub fn check_native(&self) -> std::result::Result<(), MessageError> {
118        if self.position.sequence == 0
119            || self.metadata.expected_length.is_some_and(|length| length != self.length)
120            || self.metadata.expected_sha256.is_some_and(|hash| hash != self.sha256)
121        {
122            return Err(MessageError::borrowed("file reference conflicts with its position or integrity requirements"));
123        }
124        Ok(())
125    }
126}
127
128crate::postcard_product!(FileMetadata {
129    id: crate::Identifier,
130    scope: Option<crate::Identifier>,
131    media_type: String,
132    expected_length: Option<u64>,
133    expected_sha256: Option<[u8; 32]>,
134});
135
136crate::postcard_product!(
137    FileReference { position: StorageCursor, metadata: FileMetadata, length: u64, sha256: [u8; 32] },
138    validate = FileReference::check_native
139);
140
141impl Described for FileMetadata {
142    const NAME: &'static str = concat!(module_path!(), "::", stringify!(FileMetadata));
143    const FIELDS: &'static [Field] = &[
144        Field { name: Cow::Borrowed("id"), tag: 1, kind: Kind::String, cardinality: Cardinality::Required },
145        Field { name: Cow::Borrowed("scope"), tag: 2, kind: Kind::String, cardinality: Cardinality::Optional },
146        Field { name: Cow::Borrowed("media_type"), tag: 3, kind: Kind::String, cardinality: Cardinality::Required },
147        Field { name: Cow::Borrowed("expected_length"), tag: 4, kind: Kind::U64, cardinality: Cardinality::Optional },
148        Field { name: Cow::Borrowed("expected_sha256"), tag: 5, kind: Kind::Bytes, cardinality: Cardinality::Optional },
149    ];
150
151    const MARKER: &'static [u8] = &crate::schema::declaration::encode::<
152        { crate::schema::declaration::encoded_len(Self::NAME, Self::FIELDS) },
153    >(Self::NAME, Self::FIELDS);
154}
155
156impl Described for FileReference {
157    const NAME: &'static str = concat!(module_path!(), "::", stringify!(FileReference));
158    const FIELDS: &'static [Field] = &[
159        Field {
160            name: Cow::Borrowed("position"),
161            tag: 1,
162            kind: Kind::Message { definition: StorageCursor::DEFINITION },
163            cardinality: Cardinality::Required,
164        },
165        Field {
166            name: Cow::Borrowed("metadata"),
167            tag: 2,
168            kind: Kind::Message { definition: FileMetadata::DEFINITION },
169            cardinality: Cardinality::Required,
170        },
171        Field { name: Cow::Borrowed("length"), tag: 3, kind: Kind::U64, cardinality: Cardinality::Required },
172        Field { name: Cow::Borrowed("sha256"), tag: 4, kind: Kind::Bytes, cardinality: Cardinality::Required },
173    ];
174
175    const MARKER: &'static [u8] = &crate::schema::declaration::encode::<
176        { crate::schema::declaration::encoded_len(Self::NAME, Self::FIELDS) },
177    >(Self::NAME, Self::FIELDS);
178}
179
180/* === Public Functions === */
181
182/* === Private Functions === */
183
184/* === Tests === */