Skip to main content

harmos_domain/metadata/
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;
18// Third Party Imports
19
20// Local Imports
21use super::{Identifier, Metadata};
22use crate::MessageError;
23use crate::message::{PostcardReader as Reader, PostcardValue, decoding_add};
24use crate::schema::declaration::{Definition, Field, Kind};
25use crate::schema::{Cardinality, Described};
26use crate::value::codec::{
27    ENTRY_DEFINITION, TIMESTAMP_DEFINITION, VALUE_DEPTH, entries_memory, entries_size, read_entries, read_timestamp, timestamp_size,
28    write_entries, write_timestamp,
29};
30
31/* === Constants === */
32
33const IDENTIFIER_FIELDS: &[Field] =
34    &[Field { name: Cow::Borrowed("value"), tag: 1, kind: Kind::String, cardinality: Cardinality::Required }];
35
36const METADATA_FIELDS: &[Field] = &[
37    field("identifier", 1, Kind::String, Cardinality::Required),
38    field("label", 2, Kind::String, Cardinality::Required),
39    field("description", 3, Kind::String, Cardinality::Optional),
40    field("tags", 4, Kind::String, Cardinality::Repeated),
41    field("attributes", 5, message(ENTRY_DEFINITION), Cardinality::Repeated),
42    field("created_at", 6, message(TIMESTAMP_DEFINITION), Cardinality::Optional),
43    field("updated_at", 7, message(TIMESTAMP_DEFINITION), Cardinality::Optional),
44];
45
46/* === Macros === */
47
48/* === Type Aliases === */
49
50/* === Structs === */
51
52/* === Enums === */
53
54/* === Traits === */
55
56/* === Implementations === */
57
58impl Described for Identifier {
59    const NAME: &'static str = "harmos::Identifier";
60    const FIELDS: &'static [Field] = IDENTIFIER_FIELDS;
61    const MARKER: &'static [u8] = &crate::schema::declaration::encode::<
62        { crate::schema::declaration::encoded_len(Self::NAME, Self::FIELDS) },
63    >(Self::NAME, Self::FIELDS);
64}
65
66impl Described for Metadata {
67    const NAME: &'static str = "harmos::Metadata";
68    const FIELDS: &'static [Field] = METADATA_FIELDS;
69    const MARKER: &'static [u8] = &crate::schema::declaration::encode::<
70        { crate::schema::declaration::encoded_len(Self::NAME, Self::FIELDS) },
71    >(Self::NAME, Self::FIELDS);
72}
73
74impl PostcardValue for Metadata {
75    fn write(&self, bytes: &mut Vec<u8>) {
76        self.identifier.write(bytes);
77        self.label.write(bytes);
78        self.description.write(bytes);
79        self.tags.write(bytes);
80        write_entries(&self.attributes, bytes);
81        write_instant(self.created_at, bytes);
82        write_instant(self.updated_at, bytes);
83    }
84
85    fn size(&self) -> usize {
86        self.identifier
87            .size()
88            .saturating_add(self.label.size())
89            .saturating_add(self.description.size())
90            .saturating_add(self.tags.size())
91            .saturating_add(entries_size(&self.attributes))
92            .saturating_add(instant_size(self.created_at))
93            .saturating_add(instant_size(self.updated_at))
94    }
95
96    fn memory(input: &mut Reader<'_>) -> Result<usize, MessageError> {
97        input.nested(|input| {
98            let mut owned = std::mem::size_of::<Self>();
99            for allocation in [
100                Identifier::memory(input)?.saturating_sub(std::mem::size_of::<Identifier>()),
101                String::memory(input)?.saturating_sub(std::mem::size_of::<String>()),
102                Option::<String>::memory(input)?.saturating_sub(std::mem::size_of::<Option<String>>()),
103                Vec::<String>::memory(input)?.saturating_sub(std::mem::size_of::<Vec<String>>()),
104                entries_memory(input, VALUE_DEPTH)?,
105            ] {
106                owned = decoding_add(owned, allocation)?;
107            }
108            read_instant(input)?;
109            read_instant(input)?;
110            Ok(owned)
111        })
112    }
113
114    fn read(input: &mut Reader<'_>) -> Result<Self, MessageError> {
115        input.nested(|input| {
116            let identifier = Identifier::read(input)?;
117            let label = String::read(input)?;
118            let description = Option::<String>::read(input)?;
119            let tags = Vec::<String>::read(input)?;
120            let attributes = read_entries(input, VALUE_DEPTH)?;
121            let created_at = read_instant(input)?;
122            let updated_at = read_instant(input)?;
123            let mut metadata = Self::definition(identifier, label, description, tags);
124            metadata.tags.shrink_to_fit();
125            metadata.attributes = attributes;
126            metadata.created_at = created_at;
127            metadata.updated_at = updated_at;
128            Ok(metadata)
129        })
130    }
131}
132
133/* === Public Functions === */
134
135/* === Private Functions === */
136
137const fn field(name: &'static str, tag: u32, kind: Kind, cardinality: Cardinality) -> Field {
138    Field { name: Cow::Borrowed(name), tag, kind, cardinality }
139}
140
141const fn message(definition: Definition) -> Kind {
142    Kind::Message { definition }
143}
144
145fn write_instant(value: Option<time::OffsetDateTime>, bytes: &mut Vec<u8>) {
146    value.is_some().write(bytes);
147    if let Some(value) = value {
148        write_timestamp(value, bytes);
149    }
150}
151
152fn instant_size(value: Option<time::OffsetDateTime>) -> usize {
153    1usize.saturating_add(value.map_or(0, timestamp_size))
154}
155
156fn read_instant(input: &mut Reader<'_>) -> Result<Option<time::OffsetDateTime>, MessageError> {
157    if bool::read(input)? { read_timestamp(input).map(Some) } else { Ok(None) }
158}
159
160/* === Tests === */