Skip to main content

harmos_domain/plugin/
requirement.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
19use serde::{Deserialize, Serialize};
20// Local Imports
21use crate::catalog::{Member, Visitor};
22use crate::operations::{OperationDeclaration, WireDeclaration};
23
24/* === Constants === */
25
26/* === Macros === */
27
28/* === Type Aliases === */
29
30/* === Structs === */
31
32// Permission to call one operation. An operation whose request names a channel,
33// stream or source resolves it to `scope`; any other operation is granted by
34// its id alone, which holding its requirement already confers.
35#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
36#[serde(rename_all = "snake_case", deny_unknown_fields)]
37pub struct Grant {
38    pub operation: Cow<'static, str>,
39
40    #[serde(default, skip_serializing_if = "Option::is_none")]
41    pub scope: Option<Cow<'static, str>>,
42}
43
44pub struct Requirement {
45    pub declaration: WireDeclaration,
46    pub grants: &'static [Grant],
47}
48
49pub struct Requirements;
50
51/* === Enums === */
52
53/* === Traits === */
54
55/* === Implementations === */
56
57impl Requirement {
58    pub const fn of<O: OperationDeclaration>(grants: &'static [Grant]) -> Self {
59        Self {
60            declaration: WireDeclaration::of::<O>(),
61            grants,
62        }
63    }
64}
65
66impl Grant {
67    pub fn of(operation: impl Into<Cow<'static, str>>, scope: impl Into<Cow<'static, str>>) -> Self {
68        Self {
69            operation: operation.into(),
70            scope: Some(scope.into()),
71        }
72    }
73
74    pub fn operation(&self) -> &str {
75        &self.operation
76    }
77
78    pub fn scope(&self) -> Option<&str> {
79        self.scope.as_deref()
80    }
81
82    pub(crate) const fn borrowed(&self) -> (&'static str, Option<&'static str>) {
83        let operation = match &self.operation {
84            Cow::Borrowed(value) => *value,
85            Cow::Owned(_) => panic!("const grants must be borrowed"),
86        };
87        let scope = match &self.scope {
88            None => None,
89            Some(Cow::Borrowed(value)) => Some(*value),
90            Some(Cow::Owned(_)) => panic!("const grants must be borrowed"),
91        };
92        (operation, scope)
93    }
94}
95
96impl Visitor for Requirements {
97    type Entry = Requirement;
98}
99
100impl<M> Member<M> for Requirements
101where
102    M: OperationDeclaration,
103{
104    const ENTRY: Requirement = Requirement::of::<M>(&[]);
105}
106
107/* === Public Functions === */
108
109// Upper Bound Includes Duplicates; Unused Nodes Are Empty Nested Projections.
110pub const fn callee_count(nodes: &[crate::catalog::Node<&'static [WireDeclaration]>]) -> usize {
111    let mut count = 0;
112    let mut index = 0;
113    while index < nodes.len() {
114        count += match &nodes[index] {
115            crate::catalog::Node::Leaf(entries) => entries.len(),
116            crate::catalog::Node::Nested(entries) => callee_count(entries),
117        };
118        index += 1;
119    }
120    count
121}
122
123pub const fn aggregate<const N: usize>(
124    explicit: &'static [crate::catalog::Node<Requirement>], provided: &[crate::catalog::Node<WireDeclaration>],
125    callees: &[crate::catalog::Node<&'static [WireDeclaration]>],
126) -> [crate::catalog::Node<Requirement>; N] {
127    let mut output = [const { crate::catalog::Node::Nested(&[]) }; N];
128    std::mem::forget(std::mem::replace(&mut output[0], crate::catalog::Node::Nested(explicit)));
129    let mut cursor = 1;
130    append_callees(callees, provided, &mut output, &mut cursor);
131    output
132}
133
134/* === Private Functions === */
135
136const fn same(left: &str, right: &str) -> bool {
137    let left = left.as_bytes();
138    let right = right.as_bytes();
139    if left.len() != right.len() {
140        return false;
141    }
142    let mut index = 0;
143    while index < left.len() {
144        if left[index] != right[index] {
145            return false;
146        }
147        index += 1;
148    }
149    true
150}
151
152const fn provided(nodes: &[crate::catalog::Node<WireDeclaration>], id: &str) -> bool {
153    let mut index = 0;
154    while index < nodes.len() {
155        let found = match &nodes[index] {
156            crate::catalog::Node::Leaf(entry) => same(entry.operation.id, id),
157            crate::catalog::Node::Nested(entries) => provided(entries, id),
158        };
159        if found {
160            return true;
161        }
162        index += 1;
163    }
164    false
165}
166
167const fn required(nodes: &[crate::catalog::Node<Requirement>], id: &str) -> bool {
168    let mut index = 0;
169    while index < nodes.len() {
170        let found = match &nodes[index] {
171            crate::catalog::Node::Leaf(entry) => same(entry.declaration.operation.id, id),
172            crate::catalog::Node::Nested(entries) => required(entries, id),
173        };
174        if found {
175            return true;
176        }
177        index += 1;
178    }
179    false
180}
181
182const fn append_callees(
183    nodes: &[crate::catalog::Node<&'static [WireDeclaration]>], providers: &[crate::catalog::Node<WireDeclaration>],
184    output: &mut [crate::catalog::Node<Requirement>], cursor: &mut usize,
185) {
186    let mut index = 0;
187    while index < nodes.len() {
188        match &nodes[index] {
189            crate::catalog::Node::Nested(entries) => append_callees(entries, providers, output, cursor),
190            crate::catalog::Node::Leaf(entries) => {
191                let mut member = 0;
192                while member < entries.len() {
193                    let declaration = &entries[member];
194                    let id = declaration.operation.id;
195                    if !provided(providers, id) && !required(output, id) {
196                        std::mem::forget(std::mem::replace(
197                            &mut output[*cursor],
198                            crate::catalog::Node::Leaf(Requirement {
199                                declaration: declaration.copied(),
200                                grants: &[],
201                            }),
202                        ));
203                        *cursor += 1;
204                    }
205                    member += 1;
206                }
207            }
208        }
209        index += 1;
210    }
211}
212
213/* === Tests === */