1use std::collections::BTreeMap;
2
3use sim_kernel::{Expr, Symbol};
4use sim_value::build::entry;
5
6use crate::BridgePacket;
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq)]
10pub enum ProfilePartCount {
11 ZeroOrMore,
13 OneOrMore,
15 Optional,
17 Required,
19}
20
21#[derive(Clone, Debug, PartialEq, Eq)]
23pub struct ProfilePartRule {
24 pub kind: Symbol,
26 pub count: ProfilePartCount,
28}
29
30impl ProfilePartRule {
31 pub fn new(kind: Symbol, count: ProfilePartCount) -> Self {
33 Self { kind, count }
34 }
35}
36
37#[derive(Clone, Debug, PartialEq, Eq)]
39pub struct BridgeProfileSpec {
40 pub id: Symbol,
42 pub parts: Vec<ProfilePartRule>,
44 pub one_or_more_of: Vec<Symbol>,
46}
47
48impl BridgeProfileSpec {
49 pub fn new(id: Symbol, parts: Vec<ProfilePartRule>) -> Self {
51 Self {
52 id,
53 parts,
54 one_or_more_of: Vec::new(),
55 }
56 }
57
58 pub fn one_or_more_of(id: Symbol, kinds: Vec<Symbol>) -> Self {
60 Self {
61 id,
62 parts: Vec::new(),
63 one_or_more_of: kinds,
64 }
65 }
66
67 pub fn matches_packet(&self, packet: &BridgePacket) -> bool {
69 let kinds = packet
70 .body
71 .iter()
72 .map(|part| &part.kind)
73 .collect::<Vec<_>>();
74 if !self.one_or_more_of.is_empty() {
75 return !kinds.is_empty()
76 && kinds
77 .iter()
78 .all(|kind| self.one_or_more_of.iter().any(|accepted| accepted == *kind));
79 }
80 let mut cursor = 0usize;
81 for rule in &self.parts {
82 let mut count = 0usize;
83 while cursor < kinds.len() && kinds[cursor] == &rule.kind {
84 count += 1;
85 cursor += 1;
86 }
87 match rule.count {
88 ProfilePartCount::ZeroOrMore => {}
89 ProfilePartCount::OneOrMore if count == 0 => return false,
90 ProfilePartCount::OneOrMore => {}
91 ProfilePartCount::Optional if count > 1 => return false,
92 ProfilePartCount::Optional => {}
93 ProfilePartCount::Required if count != 1 => return false,
94 ProfilePartCount::Required => {}
95 }
96 }
97 cursor == kinds.len()
98 }
99
100 pub fn to_expr(&self) -> Expr {
102 Expr::Map(vec![
103 entry("id", Expr::Symbol(self.id.clone())),
104 entry(
105 "parts",
106 if self.one_or_more_of.is_empty() {
107 Expr::Vector(
108 self.parts
109 .iter()
110 .map(|rule| {
111 Expr::Map(vec![
112 entry("kind", Expr::Symbol(rule.kind.clone())),
113 entry("count", Expr::Symbol(rule.count.symbol())),
114 ])
115 })
116 .collect(),
117 )
118 } else {
119 Expr::Vector(vec![Expr::Map(vec![
120 entry(
121 "any-of",
122 Expr::Vector(
123 self.one_or_more_of
124 .iter()
125 .cloned()
126 .map(Expr::Symbol)
127 .collect(),
128 ),
129 ),
130 entry("count", Expr::Symbol(ProfilePartCount::OneOrMore.symbol())),
131 ])])
132 },
133 ),
134 ])
135 }
136}
137
138#[derive(Clone, Debug, Default, PartialEq, Eq)]
140pub struct BridgeProfileBook {
141 specs: BTreeMap<Symbol, BridgeProfileSpec>,
142}
143
144impl BridgeProfileBook {
145 pub fn new() -> Self {
147 Self::default()
148 }
149
150 pub fn register(&mut self, spec: BridgeProfileSpec) {
152 self.specs.insert(spec.id.clone(), spec);
153 }
154
155 pub fn spec(&self, id: &Symbol) -> Option<&BridgeProfileSpec> {
157 self.specs.get(id)
158 }
159
160 pub fn specs(&self) -> impl Iterator<Item = &BridgeProfileSpec> {
162 self.specs.values()
163 }
164
165 pub fn matching_profiles(&self, packet: &BridgePacket) -> Vec<Symbol> {
167 self.specs
168 .values()
169 .filter(|spec| spec.matches_packet(packet))
170 .map(|spec| spec.id.clone())
171 .collect()
172 }
173}
174
175impl ProfilePartCount {
176 fn symbol(self) -> Symbol {
177 match self {
178 Self::ZeroOrMore => Symbol::qualified("bridge", "ZeroOrMore"),
179 Self::OneOrMore => Symbol::qualified("bridge", "OneOrMore"),
180 Self::Optional => Symbol::qualified("bridge", "Optional"),
181 Self::Required => Symbol::qualified("bridge", "Required"),
182 }
183 }
184}
185
186pub fn brief_profile_symbol() -> Symbol {
188 Symbol::qualified("bridge", "BRIEF")
189}
190
191pub fn ask_profile_symbol() -> Symbol {
193 Symbol::qualified("bridge", "ASK")
194}
195
196pub fn loom_profile_symbol() -> Symbol {
198 Symbol::qualified("bridge", "LOOM")
199}
200
201pub fn collab_profile_symbol() -> Symbol {
203 Symbol::qualified("bridge", "COLLAB")
204}
205
206pub fn bridge_profile_shape_expr() -> Expr {
208 Expr::Map(vec![
209 entry("shape", Expr::Symbol(Symbol::qualified("shape", "OneOf"))),
210 entry(
211 "choices",
212 Expr::Vector(vec![
213 Expr::Symbol(brief_profile_symbol()),
214 Expr::Symbol(ask_profile_symbol()),
215 Expr::Symbol(loom_profile_symbol()),
216 Expr::Symbol(collab_profile_symbol()),
217 ]),
218 ),
219 ])
220}
221
222pub fn brief_profile_spec() -> BridgeProfileSpec {
224 BridgeProfileSpec::new(
225 brief_profile_symbol(),
226 vec![
227 ProfilePartRule::new(part("Given"), ProfilePartCount::ZeroOrMore),
228 ProfilePartRule::new(part("Frame"), ProfilePartCount::OneOrMore),
229 ProfilePartRule::new(part("Return"), ProfilePartCount::Optional),
230 ],
231 )
232}
233
234pub fn ask_profile_spec() -> BridgeProfileSpec {
236 BridgeProfileSpec::new(
237 ask_profile_symbol(),
238 vec![
239 ProfilePartRule::new(part("Given"), ProfilePartCount::ZeroOrMore),
240 ProfilePartRule::new(part("Frame"), ProfilePartCount::ZeroOrMore),
241 ProfilePartRule::new(part("Call"), ProfilePartCount::OneOrMore),
242 ProfilePartRule::new(part("Return"), ProfilePartCount::Required),
243 ],
244 )
245}
246
247pub fn loom_profile_spec() -> BridgeProfileSpec {
249 BridgeProfileSpec::new(
250 loom_profile_symbol(),
251 vec![
252 ProfilePartRule::new(part("Given"), ProfilePartCount::ZeroOrMore),
253 ProfilePartRule::new(part("Frame"), ProfilePartCount::ZeroOrMore),
254 ProfilePartRule::new(part("Weave"), ProfilePartCount::OneOrMore),
255 ProfilePartRule::new(part("Return"), ProfilePartCount::Required),
256 ],
257 )
258}
259
260pub fn collab_profile_spec() -> BridgeProfileSpec {
262 BridgeProfileSpec::one_or_more_of(
263 collab_profile_symbol(),
264 vec![
265 part("Review"),
266 part("Vote"),
267 part("Patch"),
268 part("Evidence"),
269 part("Receipt"),
270 part("Attest"),
271 ],
272 )
273}
274
275pub fn standard_profile_book() -> BridgeProfileBook {
277 let mut book = BridgeProfileBook::new();
278 book.register(brief_profile_spec());
279 book.register(ask_profile_spec());
280 book.register(loom_profile_spec());
281 book.register(collab_profile_spec());
282 book
283}
284
285fn part(name: &str) -> Symbol {
286 Symbol::qualified("bridge", name)
287}