Skip to main content

harmos_storage/
schema_encoding.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
17
18// Third Party Imports
19use serde::de::{DeserializeSeed, Error as _, IntoDeserializer, MapAccess, Visitor};
20use serde::ser::SerializeStruct;
21use serde::{Deserialize, Deserializer, Serialize, Serializer};
22// Local Imports
23use super::manager::encoded_size;
24use super::{SchemaKey, StorageCursor, StorageSchema};
25use crate::schema::graph::Schema;
26use crate::{Error, Result};
27
28/* === Constants === */
29
30/* === Macros === */
31
32/* === Type Aliases === */
33
34/* === Structs === */
35
36struct Input<A> {
37    map: A,
38    position: Option<StorageCursor>,
39    key: Option<SchemaKey>,
40    encoding: Option<Encoding>,
41}
42
43struct StorageVisitor;
44
45/* === Enums === */
46
47#[derive(Deserialize)]
48#[serde(rename_all = "snake_case")]
49enum Encoding {
50    GraphV1,
51}
52
53/* === Traits === */
54
55/* === Implementations === */
56
57impl StorageSchema {
58    // Empty definitions have the highest allocation-to-JSON ratio: each `[]`
59    // retains a vector header and traversal state. Include vector growth and the
60    // bounded field-name diagnostic buffer before asking the backend for a page.
61    pub fn decoding_memory_bytes(encoded_bytes: usize) -> Option<usize> {
62        encoded_bytes.checked_mul(36)?.checked_add(
63            4 * std::mem::size_of::<Schema>()
64                + 4 * std::mem::size_of::<Vec<harmos_domain::schema::graph::Field>>()
65                + harmos_domain::schema::MAX_NAME_BYTES,
66        )
67    }
68
69    pub(super) fn encoded_bytes(&self) -> Result<usize> {
70        self.schema.preparation(&harmos_domain::config::SchemaSettings::default()).map_err(|_| invalid())?;
71        encoded_size(self)
72    }
73
74    pub fn encoding_memory_bytes(&self) -> Result<usize> {
75        self.schema.validation_memory_bytes(&harmos_domain::config::SchemaSettings::default()).map_err(|_| invalid())
76    }
77}
78
79impl Serialize for StorageSchema {
80    fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
81        let mut output = serializer.serialize_struct("StorageSchema", 5)?;
82        output.serialize_field("position", &self.position)?;
83        output.serialize_field("key", &self.key)?;
84        output.serialize_field("encoding", "graph_v1")?;
85        output.serialize_field("root", &self.schema.root)?;
86        output.serialize_field("definitions", &self.schema.definitions)?;
87        output.end()
88    }
89}
90
91impl<'de> Deserialize<'de> for StorageSchema {
92    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> std::result::Result<Self, D::Error> {
93        deserializer.deserialize_map(StorageVisitor).map_err(|error| D::Error::custom(format!(
94            "invalid graph_v1 storage schema: {error}; inline metadata is unsupported; retain the original data and select a fresh storage root",
95        )))
96    }
97}
98
99impl<'de> Visitor<'de> for StorageVisitor {
100    type Value = StorageSchema;
101    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
102        formatter.write_str("bounded graph_v1 storage metadata")
103    }
104    fn visit_map<A: MapAccess<'de>>(self, map: A) -> std::result::Result<Self::Value, A::Error> {
105        let mut input = Input { map, position: None, key: None, encoding: None };
106        // Forward Only The Graph Fields To The Canonical Seed Without Buffering
107        // The JSON Object Or Allocating A Second Representation Of Its Fields.
108        let schema = crate::schema::graph::Seed { settings: &harmos_domain::config::SchemaSettings::default() }
109            .deserialize(serde::de::value::MapAccessDeserializer::new(&mut input))?;
110        let Encoding::GraphV1 = input.encoding.ok_or_else(|| A::Error::missing_field("encoding"))?;
111        Ok(StorageSchema::new(
112            input.position.ok_or_else(|| A::Error::missing_field("position"))?,
113            input.key.ok_or_else(|| A::Error::missing_field("key"))?,
114            schema,
115        ))
116    }
117}
118
119impl<'de, A: MapAccess<'de>> MapAccess<'de> for Input<A> {
120    type Error = A::Error;
121    fn next_key_seed<K: DeserializeSeed<'de>>(&mut self, seed: K) -> std::result::Result<Option<K::Value>, A::Error> {
122        while let Some(key) = self.map.next_key::<String>()? {
123            match key.as_str() {
124                "position" => {
125                    if self.position.is_some() {
126                        return Err(A::Error::duplicate_field("position"));
127                    }
128                    self.position = Some(self.map.next_value()?);
129                }
130                "key" => {
131                    if self.key.is_some() {
132                        return Err(A::Error::duplicate_field("key"));
133                    }
134                    self.key = Some(self.map.next_value()?);
135                }
136                "encoding" => {
137                    if self.encoding.is_some() {
138                        return Err(A::Error::duplicate_field("encoding"));
139                    }
140                    self.encoding = Some(self.map.next_value()?);
141                }
142                _ => return seed.deserialize(key.into_deserializer()).map(Some),
143            }
144        }
145        Ok(None)
146    }
147    fn next_value_seed<V: DeserializeSeed<'de>>(&mut self, seed: V) -> std::result::Result<V::Value, A::Error> {
148        self.map.next_value_seed(seed)
149    }
150}
151
152/* === Public Functions === */
153
154/* === Private Functions === */
155
156fn invalid() -> Error {
157    Error::InvalidInput { field: "storage schema", reason: "invalid shared schema preparation" }
158}
159
160/* === Tests === */