Skip to main content

harmos_domain/operations/
catalog.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::collections::BTreeSet;
18// Third Party Imports
19
20// Local Imports
21use super::{Direction, OperationDeclaration, OperationDefinition, OperationPolicy, ReplaySafety, Visibility};
22use crate::catalog::{self, Catalog, Member, Node, Projection, Visitor};
23use crate::{Error, Identifier, Result};
24
25/* === Constants === */
26
27/* === Macros === */
28
29/* === Type Aliases === */
30
31/* === Structs === */
32
33#[derive(Clone, Copy, Debug)]
34pub struct Entry {
35    pub id: &'static str,
36    pub description: &'static str,
37    pub policy: OperationPolicy,
38    pub visibility: Visibility,
39    pub replay: ReplaySafety,
40    pub fingerprint: fn() -> Result<crate::Fingerprint>,
41}
42
43pub struct Declarations;
44
45pub struct Callees;
46
47/* === Enums === */
48
49/* === Traits === */
50
51/* === Implementations === */
52
53impl PartialEq for Entry {
54    fn eq(&self, other: &Self) -> bool {
55        self.id == other.id
56            && self.description == other.description
57            && self.policy == other.policy
58            && self.visibility == other.visibility
59            && self.replay == other.replay
60            && (self.fingerprint)().ok() == (other.fingerprint)().ok()
61    }
62}
63
64impl Eq for Entry {}
65
66impl Entry {
67    pub const fn of<O: OperationDeclaration + ?Sized>() -> Self {
68        Self {
69            id: O::ID,
70            description: O::DESCRIPTION,
71            policy: O::POLICY,
72            visibility: O::VISIBILITY,
73            replay: O::REPLAY,
74            fingerprint: fingerprint::<O>,
75        }
76    }
77
78    pub fn definition(&self) -> Result<OperationDefinition> {
79        let definition = self.declared()?;
80        definition.validate()?;
81        Ok(definition)
82    }
83
84    pub(super) fn declared(&self) -> Result<OperationDefinition> {
85        let id = Identifier::new(self.id)?;
86        let definition = OperationDefinition {
87            id,
88            description: self.description.to_owned(),
89            policy: self.policy,
90            visibility: self.visibility,
91            replay: self.replay,
92            fingerprint: (self.fingerprint)()?,
93        };
94        Ok(definition)
95    }
96}
97
98impl Visitor for Declarations {
99    type Entry = Entry;
100}
101
102impl<M: OperationDeclaration> Member<M> for Declarations {
103    const ENTRY: Entry = Entry::of::<M>();
104}
105
106impl Declarations {
107    pub fn of<C: Catalog<Self>>() -> Result<Vec<OperationDefinition>> {
108        Self::from_projection(&C::ENTRIES)
109    }
110
111    pub fn from_entries(entries: &[Entry]) -> Result<Vec<OperationDefinition>> {
112        let mut names = BTreeSet::new();
113        let mut definitions = Vec::with_capacity(entries.len());
114        for declaration in entries {
115            append(declaration, &mut names, &mut definitions)?;
116        }
117        Ok(definitions)
118    }
119
120    pub fn from_tree(entries: &[Node<Entry>]) -> Result<Vec<OperationDefinition>> {
121        let mut names = BTreeSet::new();
122        let mut definitions = Vec::with_capacity(catalog::count(entries));
123        catalog::try_for_each(entries, |declaration| append(declaration, &mut names, &mut definitions))?;
124        Ok(definitions)
125    }
126
127    pub fn from_projection<P: Projection<Entry>>(entries: &P) -> Result<Vec<OperationDefinition>> {
128        let mut names = BTreeSet::new();
129        let mut definitions = Vec::with_capacity(P::LEN);
130        entries.try_for_each(|declaration| append(declaration, &mut names, &mut definitions))?;
131        Ok(definitions)
132    }
133}
134
135impl Visitor for Callees {
136    type Entry = &'static [super::WireDeclaration];
137}
138
139impl<M: OperationDeclaration> Member<M> for Callees {
140    const ENTRY: Self::Entry = M::OPERATIONS;
141}
142
143/* === Public Functions === */
144
145/* === Private Functions === */
146
147fn payload<T: crate::Portable>() -> Result<crate::Fingerprint> {
148    let schema = crate::schema::graph::Schema::from_declaration(T::DEFINITION, &crate::config::SchemaSettings::default())
149        .map_err(|error| crate::MessageError::new(error.to_string()))?;
150    let root = schema.root().map_err(|error| crate::MessageError::new(error.to_string()))?;
151    crate::schema::identity::fingerprint(root, T::CODEC).map_err(|error| crate::MessageError::new(error.to_string()).into())
152}
153
154fn fingerprint<O: OperationDeclaration + ?Sized>() -> Result<crate::Fingerprint> {
155    let input = if O::Input::STREAMED { Some(payload::<<O::Input as super::Direction>::Item>()?) } else { None };
156    let output = if O::Output::STREAMED { Some(payload::<<O::Output as super::Direction>::Item>()?) } else { None };
157    Ok(crate::schema::identity::operation(O::ID, payload::<O::Request>()?, payload::<O::Response>()?, input, output))
158}
159
160fn append(declaration: &Entry, names: &mut BTreeSet<Identifier>, definitions: &mut Vec<OperationDefinition>) -> Result<()> {
161    let definition = declaration.definition()?;
162    if !names.insert(definition.id.clone()) {
163        return Err(Error::DuplicateOperation { id: declaration.id.to_owned() });
164    }
165    definitions.push(definition);
166    Ok(())
167}
168
169/* === Tests === */