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