use std::collections::BTreeMap;
use sim_kernel::{Expr, Symbol};
use sim_value::build::entry;
use crate::BridgePacket;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ProfilePartCount {
ZeroOrMore,
OneOrMore,
Optional,
Required,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ProfilePartRule {
pub kind: Symbol,
pub count: ProfilePartCount,
}
impl ProfilePartRule {
pub fn new(kind: Symbol, count: ProfilePartCount) -> Self {
Self { kind, count }
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BridgeProfileSpec {
pub id: Symbol,
pub parts: Vec<ProfilePartRule>,
pub one_or_more_of: Vec<Symbol>,
}
impl BridgeProfileSpec {
pub fn new(id: Symbol, parts: Vec<ProfilePartRule>) -> Self {
Self {
id,
parts,
one_or_more_of: Vec::new(),
}
}
pub fn one_or_more_of(id: Symbol, kinds: Vec<Symbol>) -> Self {
Self {
id,
parts: Vec::new(),
one_or_more_of: kinds,
}
}
pub fn matches_packet(&self, packet: &BridgePacket) -> bool {
let kinds = packet
.body
.iter()
.map(|part| &part.kind)
.collect::<Vec<_>>();
if !self.one_or_more_of.is_empty() {
return !kinds.is_empty()
&& kinds
.iter()
.all(|kind| self.one_or_more_of.iter().any(|accepted| accepted == *kind));
}
let mut cursor = 0usize;
for rule in &self.parts {
let mut count = 0usize;
while cursor < kinds.len() && kinds[cursor] == &rule.kind {
count += 1;
cursor += 1;
}
match rule.count {
ProfilePartCount::ZeroOrMore => {}
ProfilePartCount::OneOrMore if count == 0 => return false,
ProfilePartCount::OneOrMore => {}
ProfilePartCount::Optional if count > 1 => return false,
ProfilePartCount::Optional => {}
ProfilePartCount::Required if count != 1 => return false,
ProfilePartCount::Required => {}
}
}
cursor == kinds.len()
}
pub fn to_expr(&self) -> Expr {
Expr::Map(vec![
entry("id", Expr::Symbol(self.id.clone())),
entry(
"parts",
if self.one_or_more_of.is_empty() {
Expr::Vector(
self.parts
.iter()
.map(|rule| {
Expr::Map(vec![
entry("kind", Expr::Symbol(rule.kind.clone())),
entry("count", Expr::Symbol(rule.count.symbol())),
])
})
.collect(),
)
} else {
Expr::Vector(vec![Expr::Map(vec![
entry(
"any-of",
Expr::Vector(
self.one_or_more_of
.iter()
.cloned()
.map(Expr::Symbol)
.collect(),
),
),
entry("count", Expr::Symbol(ProfilePartCount::OneOrMore.symbol())),
])])
},
),
])
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct BridgeProfileBook {
specs: BTreeMap<Symbol, BridgeProfileSpec>,
}
impl BridgeProfileBook {
pub fn new() -> Self {
Self::default()
}
pub fn register(&mut self, spec: BridgeProfileSpec) {
self.specs.insert(spec.id.clone(), spec);
}
pub fn spec(&self, id: &Symbol) -> Option<&BridgeProfileSpec> {
self.specs.get(id)
}
pub fn specs(&self) -> impl Iterator<Item = &BridgeProfileSpec> {
self.specs.values()
}
pub fn matching_profiles(&self, packet: &BridgePacket) -> Vec<Symbol> {
self.specs
.values()
.filter(|spec| spec.matches_packet(packet))
.map(|spec| spec.id.clone())
.collect()
}
}
impl ProfilePartCount {
fn symbol(self) -> Symbol {
match self {
Self::ZeroOrMore => Symbol::qualified("bridge", "ZeroOrMore"),
Self::OneOrMore => Symbol::qualified("bridge", "OneOrMore"),
Self::Optional => Symbol::qualified("bridge", "Optional"),
Self::Required => Symbol::qualified("bridge", "Required"),
}
}
}
pub fn brief_profile_symbol() -> Symbol {
Symbol::qualified("bridge", "BRIEF")
}
pub fn ask_profile_symbol() -> Symbol {
Symbol::qualified("bridge", "ASK")
}
pub fn loom_profile_symbol() -> Symbol {
Symbol::qualified("bridge", "LOOM")
}
pub fn collab_profile_symbol() -> Symbol {
Symbol::qualified("bridge", "COLLAB")
}
pub fn bridge_profile_shape_expr() -> Expr {
Expr::Map(vec![
entry("shape", Expr::Symbol(Symbol::qualified("shape", "OneOf"))),
entry(
"choices",
Expr::Vector(vec![
Expr::Symbol(brief_profile_symbol()),
Expr::Symbol(ask_profile_symbol()),
Expr::Symbol(loom_profile_symbol()),
Expr::Symbol(collab_profile_symbol()),
]),
),
])
}
pub fn brief_profile_spec() -> BridgeProfileSpec {
BridgeProfileSpec::new(
brief_profile_symbol(),
vec![
ProfilePartRule::new(part("Given"), ProfilePartCount::ZeroOrMore),
ProfilePartRule::new(part("Frame"), ProfilePartCount::OneOrMore),
ProfilePartRule::new(part("Return"), ProfilePartCount::Optional),
],
)
}
pub fn ask_profile_spec() -> BridgeProfileSpec {
BridgeProfileSpec::new(
ask_profile_symbol(),
vec![
ProfilePartRule::new(part("Given"), ProfilePartCount::ZeroOrMore),
ProfilePartRule::new(part("Frame"), ProfilePartCount::ZeroOrMore),
ProfilePartRule::new(part("Call"), ProfilePartCount::OneOrMore),
ProfilePartRule::new(part("Return"), ProfilePartCount::Required),
],
)
}
pub fn loom_profile_spec() -> BridgeProfileSpec {
BridgeProfileSpec::new(
loom_profile_symbol(),
vec![
ProfilePartRule::new(part("Given"), ProfilePartCount::ZeroOrMore),
ProfilePartRule::new(part("Frame"), ProfilePartCount::ZeroOrMore),
ProfilePartRule::new(part("Weave"), ProfilePartCount::OneOrMore),
ProfilePartRule::new(part("Return"), ProfilePartCount::Required),
],
)
}
pub fn collab_profile_spec() -> BridgeProfileSpec {
BridgeProfileSpec::one_or_more_of(
collab_profile_symbol(),
vec![
part("Review"),
part("Vote"),
part("Patch"),
part("Evidence"),
part("Receipt"),
part("Attest"),
],
)
}
pub fn standard_profile_book() -> BridgeProfileBook {
let mut book = BridgeProfileBook::new();
book.register(brief_profile_spec());
book.register(ask_profile_spec());
book.register(loom_profile_spec());
book.register(collab_profile_spec());
book
}
fn part(name: &str) -> Symbol {
Symbol::qualified("bridge", name)
}