1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
//! Transport-agnostic frame dispatch.
//!
//! [`dispatch_frame`] is the single function both transport layers call for
//! every received NERVE frame. It reads the decoded frame, updates the
//! [`crate::RequestTable`] as needed, and returns a [`DispatchAction`] telling
//! the transport what to do next.
//!
//! `dispatch_frame` performs no I/O. All socket reads and writes are the
//! responsibility of the calling transport layer ([`crate::server`] or
//! [`crate::ws_server`]).
//!
//! # AI daemon boundary
//!
//! When a `SearchQuery` frame is received, `dispatch_frame` registers the
//! request in the [`crate::RequestTable`] and returns
//! [`DispatchAction::ForwardToAiDaemon`]. The AI daemon (a future milestone)
//! is responsible for consuming the query, checking cancellation, and streaming
//! results back. The transport loop must not send any reply itself for this
//! variant.
use encode;
use ;
use ;
use crateRequestTable;
/// Outcome returned to the connection loop after dispatching a frame.
///
/// Marked `#[non_exhaustive]` because new variants will be added as the AI
/// daemon layer is built out. Downstream code must include a wildcard arm
/// when matching on this type.
///
/// # Examples
///
/// ```
/// use nerve_ipc_core::DispatchAction;
///
/// fn handle(action: DispatchAction) -> Option<Vec<u8>> {
/// match action {
/// DispatchAction::Handled => None,
/// DispatchAction::Reply(bytes) => Some(bytes),
/// DispatchAction::ForwardToAiDaemon(_) => None,
/// // Required: new variants will be added as the AI daemon is built out.
/// _ => None,
/// }
/// }
/// ```
/// Dispatch a single decoded frame and return the action to take.
///
/// This function is transport-agnostic: it does not perform any I/O.
/// The caller is responsible for sending any [`DispatchAction::Reply`] bytes
/// back to the client, whether over a Unix socket or a WebSocket.
///
/// Must be fast, non-blocking, and deterministic.
///
/// # Errors
///
/// Returns [`ProtocolError`] only when encoding the `Ping` reply fails. All
/// other message types either produce no reply or register state in the
/// [`RequestTable`] without encoding a response.
///
/// # Examples
///
/// ```
/// use nerve_ipc_core::{dispatch_frame, DispatchAction, RequestTable};
/// use nerve_protocol::codec::{decode, encode};
/// use nerve_protocol::types::{FrameFlags, MessageType, RequestId};
///
/// let bytes = encode(MessageType::Ping, FrameFlags::empty(), RequestId(1), b"").unwrap();
/// let frame = decode(&bytes).unwrap();
/// let mut table = RequestTable::new();
///
/// match dispatch_frame(frame, &mut table).unwrap() {
/// DispatchAction::Reply(_reply) => { /* send reply to the client */ }
/// DispatchAction::Handled => {}
/// DispatchAction::ForwardToAiDaemon(_) => {}
/// _ => {}
/// }
/// ```