1use std::collections::{HashMap, HashSet};
17use std::sync::Arc;
18
19use bynk_ide::agent_effects::{self, HandlerEffects};
20use bynk_ide::agent_state::{self, AgentStateModel, Certainty, Target};
21use bynk_syntax::ast::{AgentDecl, CommonsItem, SourceUnit, TypeDecl};
22use bynk_syntax::span::Span;
23use tower_lsp::lsp_types::Range;
24
25#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
29#[serde(rename_all = "camelCase")]
30pub struct AgentStateModelParams {
31 pub text_document: tower_lsp::lsp_types::TextDocumentIdentifier,
32 pub position: tower_lsp::lsp_types::Position,
33}
34
35fn agents(text: &str) -> Vec<AgentDecl> {
38 let Ok(tokens) = bynk_syntax::lexer::tokenize(text) else {
39 return Vec::new();
40 };
41 let (unit, _errs) = bynk_syntax::parser::parse_unit_with_recovery(&tokens, text);
42 let items = match unit {
43 Some(SourceUnit::Context(c)) => c.items,
44 Some(SourceUnit::Adapter(a)) => a.items,
45 _ => return Vec::new(),
46 };
47 items
48 .into_iter()
49 .filter_map(|i| match i {
50 CommonsItem::Agent(a) => Some(a),
51 _ => None,
52 })
53 .collect()
54}
55
56pub fn agent_state_at(
59 text: &str,
60 offset: usize,
61 types: &HashMap<String, Arc<TypeDecl>>,
62) -> Option<(AgentDecl, AgentStateModel)> {
63 let agent = agents(text)
64 .into_iter()
65 .find(|a| a.span.start <= offset && offset < a.span.end)?;
66 let model = agent_state::agent_state_model(&agent, types);
67 Some((agent, model))
68}
69
70pub fn agent_lens_sites(text: &str) -> Vec<Span> {
72 agents(text).iter().map(|a| a.name.span).collect()
73}
74
75#[derive(Debug, Clone, serde::Serialize)]
76pub struct WireAgentStateModel {
77 pub agent: String,
78 pub range: Range,
79 pub handlers: Vec<WireHandler>,
80 pub machines: Vec<WireStateMachine>,
81 pub effects: Vec<WireHandlerEffects>,
83}
84
85#[derive(Debug, Clone, serde::Serialize)]
86pub struct WireHandlerEffects {
87 pub handler: String,
88 pub rows: Vec<WireEffectRow>,
89 pub truncated: bool,
90}
91
92#[derive(Debug, Clone, serde::Serialize)]
93pub struct WireEffectRow {
94 pub guards: Vec<String>,
95 pub writes: Vec<WireEffectWrite>,
96 pub reads: Vec<String>,
97 pub calls: Vec<String>,
98 pub outcomes: Vec<String>,
99}
100
101#[derive(Debug, Clone, serde::Serialize)]
102pub struct WireEffectWrite {
103 pub field: String,
104 pub op: String,
105 pub key: Option<String>,
106 pub range: Range,
107}
108
109pub fn effects_of(agent: &AgentDecl, agents: &HashSet<String>) -> Vec<HandlerEffects> {
112 agent_effects::agent_effects(agent, agents)
113}
114
115#[derive(Debug, Clone, serde::Serialize)]
116pub struct WireHandler {
117 pub name: String,
118 pub range: Range,
119}
120
121#[derive(Debug, Clone, serde::Serialize)]
122#[serde(rename_all = "camelCase")]
123pub struct WireStateMachine {
124 pub field: String,
125 pub field_range: Range,
126 pub states: Vec<String>,
127 pub initial: Option<String>,
128 pub edges: Vec<WireEdge>,
129 pub reachable: Vec<String>,
130 pub reachable_after_commit: Vec<String>,
134 pub truncated: Vec<String>,
135 pub not_evaluated: Vec<WireNamed>,
137}
138
139#[derive(Debug, Clone, serde::Serialize)]
140pub struct WireNamed {
141 pub name: String,
142 pub range: Range,
143}
144
145#[derive(Debug, Clone, serde::Serialize)]
147pub struct WireViolation {
148 pub transition: String,
149 pub range: Range,
150 pub certainty: &'static str,
152 pub from: Vec<String>,
153 pub targets: Vec<String>,
154}
155
156#[derive(Debug, Clone, serde::Serialize)]
157pub struct WireEdge {
158 pub handler: String,
159 pub from: Vec<String>,
160 pub guards: Vec<String>,
161 pub to: WireTarget,
164 pub outcome: Option<String>,
165 pub range: Range,
166 pub violations: Vec<WireViolation>,
167}
168
169#[derive(Debug, Clone, serde::Serialize)]
170#[serde(tag = "kind")]
171pub enum WireTarget {
172 State { state: String },
173 Unknown,
174 Unchanged,
175}
176
177pub fn to_wire(
178 agent: &AgentDecl,
179 model: &AgentStateModel,
180 effects: &[HandlerEffects],
181 text: &str,
182) -> WireAgentStateModel {
183 let range = |s: Span| crate::position::span_to_range(text, s);
184 WireAgentStateModel {
185 effects: effects
186 .iter()
187 .map(|h| WireHandlerEffects {
188 handler: h.handler.clone(),
189 truncated: h.truncated,
190 rows: h
191 .rows
192 .iter()
193 .map(|r| WireEffectRow {
194 guards: r.guards.clone(),
195 writes: r
196 .writes
197 .iter()
198 .map(|w| WireEffectWrite {
199 field: w.field.clone(),
200 op: w.op.clone(),
201 key: w.key.clone(),
202 range: range(w.span),
203 })
204 .collect(),
205 reads: r.reads.clone(),
206 calls: r.calls.clone(),
207 outcomes: r.outcomes.clone(),
208 })
209 .collect(),
210 })
211 .collect(),
212 agent: model.agent.clone(),
213 range: range(agent.name.span),
214 handlers: agent
215 .handlers
216 .iter()
217 .map(|h| WireHandler {
218 name: h
219 .method_name
220 .as_ref()
221 .map_or_else(|| "call".to_string(), |m| m.name.clone()),
222 range: range(h.method_name.as_ref().map_or(h.span, |m| m.span)),
223 })
224 .collect(),
225 machines: model
226 .machines
227 .iter()
228 .map(|m| WireStateMachine {
229 field: m.field.clone(),
230 field_range: range(m.field_span),
231 states: m.states.clone(),
232 initial: m.initial.clone(),
233 edges: m
234 .edges
235 .iter()
236 .map(|e| WireEdge {
237 handler: e.handler.clone(),
238 from: e.from.clone(),
239 guards: e.guards.clone(),
240 to: match &e.to {
241 Target::State(s) => WireTarget::State { state: s.clone() },
242 Target::Unknown => WireTarget::Unknown,
243 Target::Unchanged => WireTarget::Unchanged,
244 },
245 outcome: e.outcome.clone(),
246 range: range(e.span),
247 violations: e
248 .violations
249 .iter()
250 .map(|v| WireViolation {
251 transition: v.transition.clone(),
252 range: range(v.span),
253 certainty: match v.certainty {
254 Certainty::Known => "Known",
255 Certainty::Possible => "Possible",
256 },
257 from: v.from.clone(),
258 targets: v.targets.clone(),
259 })
260 .collect(),
261 })
262 .collect(),
263 reachable: m.reachable.clone(),
264 reachable_after_commit: m.reachable_after_commit.clone(),
265 truncated: m.truncated.clone(),
266 not_evaluated: m
267 .not_evaluated
268 .iter()
269 .map(|n| WireNamed {
270 name: n.transition.clone(),
271 range: range(n.span),
272 })
273 .collect(),
274 })
275 .collect(),
276 }
277}