sim_lib_bridge/
frontier.rs1use std::sync::Arc;
2
3use sim_codec_bridge::{BridgeBook, BridgeFramePayload, BridgePacket};
4use sim_kernel::{Cx, Expr, Result, Symbol};
5use sim_lib_agent_runner_core::shape_to_grammar;
6use sim_shape::{ExactExprShape, OneOfShape, Shape};
7use sim_value::build::entry;
8
9pub struct FrontierMenu {
11 pub slots: Vec<(String, Expr)>,
13 pub heads: Expr,
15 pub grammar: String,
17}
18
19pub fn frontier(_cx: &mut Cx, packet: &BridgePacket) -> Result<FrontierMenu> {
21 let book = BridgeBook::standard();
22 let slots = frame_slots(&book, packet)?;
23 let heads = book
24 .moves
25 .legal_reply_intents(std::slice::from_ref(&packet.header.move_kind));
26 let head_shape = OneOfShape::new(
27 heads
28 .iter()
29 .map(|head| Arc::new(ExactExprShape::new(Expr::Symbol(head.clone()))) as Arc<dyn Shape>)
30 .collect(),
31 );
32 let grammar = shape_to_grammar(&head_shape)?;
33 Ok(FrontierMenu {
34 slots,
35 heads: Expr::Map(vec![
36 entry("shape", Expr::Symbol(Symbol::qualified("shape", "OneOf"))),
37 entry(
38 "choices",
39 Expr::Vector(heads.into_iter().map(Expr::Symbol).collect()),
40 ),
41 ]),
42 grammar,
43 })
44}
45
46fn frame_slots(book: &BridgeBook, packet: &BridgePacket) -> Result<Vec<(String, Expr)>> {
47 let mut slots = Vec::new();
48 for part in &packet.body {
49 if part.kind != Symbol::qualified("bridge", "Frame") {
50 continue;
51 }
52 let payload = BridgeFramePayload::from_expr(&part.payload)?;
53 let spec = book.frames.require_spec(&payload.frame)?;
54 for hole in &spec.holes {
55 slots.push((
56 format!(
57 "{}.{}",
58 part.id.as_qualified_str(),
59 hole.name.as_qualified_str()
60 ),
61 hole.kind.shape_expr(),
62 ));
63 }
64 }
65 Ok(slots)
66}