1use crate::context::AppContext;
2use crate::protocol::Response;
3
4pub fn finalize_response(
6 response: &mut Response,
7 ctx: &AppContext,
8 session_id: &str,
9 attach_command: &str,
10) {
11 finalize_response_with_bg_completions(response, ctx, session_id, attach_command, true);
12}
13
14pub fn finalize_response_with_bg_completions(
15 response: &mut Response,
16 ctx: &AppContext,
17 session_id: &str,
18 attach_command: &str,
19 allow_bg_completions: bool,
20) {
21 if allow_bg_completions {
22 attach_bg_completions(response, ctx, session_id, attach_command);
23 }
24 attach_status_bar(response, ctx, attach_command);
25}
26
27pub enum DispatchOutcome {
28 Immediate(Response),
29 Deferred(PendingResponse),
30}
31
32pub type PendingResponsePoll = Box<dyn FnMut(&AppContext) -> Option<Response>>;
33
34pub struct PendingResponse {
35 pub request_id: String,
36 pub session_id: String,
37 pub attach_command: String,
38 pub poll: PendingResponsePoll,
39}
40
41pub struct ResolvedPending {
42 pub response: Response,
43 pub session_id: String,
44 pub attach_command: String,
45}
46
47#[derive(Default)]
48pub struct PendingResponses {
49 entries: Vec<PendingResponse>,
50}
51
52impl PendingResponses {
53 pub fn register(&mut self, pending: PendingResponse) {
54 self.entries
55 .retain(|entry| entry.request_id != pending.request_id);
56 self.entries.push(pending);
57 }
58
59 pub fn poll_ready(&mut self, ctx: &AppContext) -> Vec<ResolvedPending> {
60 let mut ready = Vec::new();
61 let mut waiting = Vec::with_capacity(self.entries.len());
62
63 for mut pending in self.entries.drain(..) {
64 if let Some(response) = (pending.poll)(ctx) {
65 ready.push(ResolvedPending {
66 response,
67 session_id: pending.session_id,
68 attach_command: pending.attach_command,
69 });
70 } else {
71 waiting.push(pending);
72 }
73 }
74
75 self.entries = waiting;
76 ready
77 }
78
79 pub fn is_empty(&self) -> bool {
80 self.entries.is_empty()
81 }
82
83 pub fn drain_on_shutdown(&mut self) {
84 self.entries.clear();
85 }
86}
87
88pub fn attach_bg_completions(
89 response: &mut Response,
90 ctx: &AppContext,
91 session_id: &str,
92 command: &str,
93) {
94 if matches!(
95 command,
96 "configure"
97 | "bash_status"
98 | "bash_write"
99 | "bash_promote"
100 | "bash_regex_match"
101 | "bash_drain_completions"
102 | "bash_notify"
103 | "bash_unnotify"
104 | "bash_ack_completions"
105 ) {
106 return;
107 }
108 if !ctx
109 .bash_background()
110 .has_completions_for_session(Some(session_id))
111 {
112 return;
113 }
114 let completions = ctx
115 .bash_background()
116 .drain_completions_for_session(Some(session_id));
117 if completions.is_empty() {
118 return;
119 }
120 let value = serde_json::json!(completions);
121 match response.data.as_object_mut() {
122 Some(data) => {
123 data.insert("bg_completions".to_string(), value);
124 }
125 None => {
126 response.data = serde_json::json!({ "bg_completions": value });
127 }
128 }
129}
130
131pub fn attach_status_bar(response: &mut Response, ctx: &AppContext, command: &str) {
138 if response
143 .data
144 .as_object_mut()
145 .and_then(|data| data.remove("_aft_suppress_status_bar"))
146 .is_some()
147 {
148 return;
149 }
150 if matches!(
151 command,
152 "configure"
153 | "ping"
154 | "version"
155 | "status"
156 | "bash_status"
157 | "bash_write"
158 | "bash_promote"
159 | "bash_regex_match"
160 | "bash_drain_completions"
161 | "bash_notify"
162 | "bash_unnotify"
163 | "bash_ack_completions"
164 ) {
165 return;
166 }
167 let Some(counts) = ctx.status_bar_counts() else {
168 return;
169 };
170 if !ctx.should_emit_status_bar(&counts) {
171 return;
172 }
173 let value = serde_json::json!({
174 "errors": counts.errors,
175 "warnings": counts.warnings,
176 "dead_code": counts.dead_code,
177 "unused_exports": counts.unused_exports,
178 "duplicates": counts.duplicates,
179 "todos": counts.todos,
180 "tier2_stale": counts.tier2_stale,
181 });
182 match response.data.as_object_mut() {
183 Some(data) => {
184 data.insert("status_bar".to_string(), value);
185 }
186 None => {
187 response.data = serde_json::json!({ "status_bar": value });
188 }
189 }
190}