Skip to main content

macroonz_compiler/recipe/
encode.rs

1//! Canonical recipe and final-emission content.
2
3use super::types::{ProjectionStanding, RecipeRelationPayload, RecipeShellContent};
4use super::{Recipe, RecipeRelationRequirements, RecipeRole, RecipeTransitionEffect};
5use crate::identity::{encode_bytes, encode_length};
6use crate::kind::{CanonicalContent, Role};
7
8impl CanonicalContent for Recipe {
9    fn encode_content_into(&self, into: &mut Vec<u8>) {
10        encode_bytes(self.module_name().as_bytes(), into);
11        encode_bytes(&self.module_head().canonical_bytes(), into);
12        encode_bytes(&self.authored_body().canonical_bytes(), into);
13        encode_length(self.vocabularies().count(), into);
14        for vocabulary in self.vocabularies() {
15            encode_bytes(vocabulary.name().as_bytes(), into);
16            encode_length(vocabulary.members().count(), into);
17            for member in vocabulary.members().members() {
18                encode_bytes(member.spelling().as_bytes(), into);
19            }
20        }
21        encode_length(self.relations().count(), into);
22        for relation in self.relations() {
23            encode_bytes(relation.name().as_bytes(), into);
24            encode_bytes(relation.left_vocabulary().as_bytes(), into);
25            encode_bytes(relation.right_vocabulary().as_bytes(), into);
26            encode_bytes(relation.payload_kind().name().as_bytes(), into);
27            encode_length(relation.row_count(), into);
28            for row in relation.rows() {
29                encode_bytes(row.left().as_bytes(), into);
30                encode_bytes(row.right().as_bytes(), into);
31                encode_relation_payload(row.payload(), into);
32            }
33            encode_relation_requirements(relation.requirements(), into);
34        }
35        encode_length(self.codecs().count(), into);
36        for codec in self.codecs() {
37            encode_bytes(codec.name().as_bytes(), into);
38            encode_bytes(&codec.content().canonical_content_bytes(), into);
39        }
40        encode_optional_name(
41            self.transition_relation().map(super::RecipeRelation::name),
42            into,
43        );
44        for role in RecipeRole::ALL {
45            encode_bytes(role.name().as_bytes(), into);
46            match self.standing(*role) {
47                ProjectionStanding::NotRequested => into.push(0),
48                ProjectionStanding::Generated(lowering) => {
49                    into.push(1);
50                    encode_lowering(lowering, into);
51                }
52                ProjectionStanding::FeatureUnavailable => into.push(2),
53                ProjectionStanding::TargetUnavailable => into.push(3),
54            }
55            encode_bytes(role.destination().name().as_bytes(), into);
56            encode_evidence(self.evidence(*role), into);
57        }
58        match self.support() {
59            None => into.push(0),
60            Some(address) => {
61                into.push(1);
62                encode_bytes(address.spelling().as_bytes(), into);
63            }
64        }
65    }
66}
67
68fn encode_relation_payload(payload: &RecipeRelationPayload, into: &mut Vec<u8>) {
69    match payload {
70        RecipeRelationPayload::Unlabeled => into.push(0),
71        RecipeRelationPayload::Path(path) => {
72            into.push(1);
73            encode_bytes(&path.canonical_bytes(), into);
74        }
75        RecipeRelationPayload::ExactRust(exact) => {
76            into.push(2);
77            encode_bytes(&exact.canonical_bytes(), into);
78        }
79        RecipeRelationPayload::Transition { target, effect, .. } => {
80            into.push(3);
81            encode_bytes(target.as_bytes(), into);
82            match effect {
83                RecipeTransitionEffect::Path(path) => {
84                    into.push(0);
85                    encode_bytes(&path.canonical_bytes(), into);
86                }
87                RecipeTransitionEffect::ExactRust {
88                    target_binding,
89                    body,
90                } => {
91                    into.push(1);
92                    target_binding.encode_into(into);
93                    encode_bytes(&body.canonical_bytes(), into);
94                }
95            }
96        }
97    }
98}
99
100fn encode_relation_requirements(requirements: &RecipeRelationRequirements, into: &mut Vec<u8>) {
101    encode_optional_name(
102        requirements
103            .empty()
104            .map(crate::relation::EmptyPosture::name),
105        into,
106    );
107    encode_optional_name(
108        requirements
109            .repetition()
110            .map(crate::relation::RepetitionPosture::name),
111        into,
112    );
113    encode_optional_name(
114        requirements
115            .left_membership()
116            .map(crate::relation::MembershipPosture::name),
117        into,
118    );
119    encode_optional_name(
120        requirements
121            .right_membership()
122            .map(crate::relation::MembershipPosture::name),
123        into,
124    );
125    encode_optional_name(
126        requirements
127            .left_completeness()
128            .map(crate::relation::CompletenessPosture::name),
129        into,
130    );
131    encode_optional_name(
132        requirements
133            .right_completeness()
134            .map(crate::relation::CompletenessPosture::name),
135        into,
136    );
137    encode_optional_name(
138        requirements
139            .density()
140            .map(crate::relation::DensityPosture::name),
141        into,
142    );
143    encode_optional_name(
144        requirements
145            .absence()
146            .map(crate::relation::AbsencePosture::name),
147        into,
148    );
149    encode_optional_name(
150        requirements
151            .self_relation()
152            .map(crate::relation::SelfRelationPosture::name),
153        into,
154    );
155    encode_optional_name(
156        requirements
157            .cycle()
158            .map(crate::relation::CyclePosture::name),
159        into,
160    );
161}
162
163fn encode_lowering(lowering: &super::EffectiveProjection, into: &mut Vec<u8>) {
164    encode_bytes(lowering.source().name().as_bytes(), into);
165    encode_optional_name(lowering.name(), into);
166    encode_optional_name(lowering.subject(), into);
167    match lowering.dispatch_bindings() {
168        None => into.push(0),
169        Some([state, event]) => {
170            into.push(1);
171            encode_bytes(state.as_bytes(), into);
172            encode_bytes(event.as_bytes(), into);
173        }
174    }
175    let relation_tables = lowering.relation_tables().collect::<Vec<_>>();
176    encode_length(relation_tables.len(), into);
177    for table in relation_tables {
178        encode_bytes(table.relation().as_bytes(), into);
179        encode_bytes(table.function().as_bytes(), into);
180        encode_bytes(table.source().name().as_bytes(), into);
181        match table.exact_rust() {
182            None => into.push(0),
183            Some(exact) => {
184                into.push(1);
185                encode_bytes(&exact.canonical_bytes(), into);
186            }
187        }
188    }
189    let Some(exact) = lowering.exact_rust() else {
190        return;
191    };
192    encode_bytes(&exact.canonical_bytes(), into);
193}
194
195fn encode_evidence(evidence: Option<&super::RecipeEvidence>, into: &mut Vec<u8>) {
196    let Some(evidence) = evidence else {
197        into.push(0);
198        return;
199    };
200    into.push(1);
201    encode_optional_name(evidence.target().map(super::EvidenceTarget::name), into);
202    encode_bytes(&evidence.body().canonical_bytes(), into);
203}
204
205fn encode_optional_name(name: Option<&str>, into: &mut Vec<u8>) {
206    match name {
207        None => into.push(0),
208        Some(name) => {
209            into.push(1);
210            encode_bytes(name.as_bytes(), into);
211        }
212    }
213}
214
215impl CanonicalContent for RecipeShellContent {
216    fn encode_content_into(&self, into: &mut Vec<u8>) {
217        encode_bytes(self.recipe.as_bytes(), into);
218        match self.support {
219            None => into.push(0),
220            Some(support) => {
221                into.push(1);
222                encode_bytes(support.as_bytes(), into);
223            }
224        }
225    }
226}