Skip to main content

pg_proto/
grammar.rs

1//! Generated `PostgreSQL` grammars and differential-test runtime FSMs.
2//!
3//! Each generated role module embeds its railroad diagram directly in its
4//! rustdoc landing page. Open a module such as [`frontend`] or [`backend`] to
5//! review the grammar alongside its generated transition API.
6
7use pg_proto_fsm::protocol;
8
9protocol! {
10    pub mod frontend {
11        initial Ready;
12        messages {
13            internal: crate::codec::FrontendMessage,
14            external: crate::codec::BackendMessage,
15        }
16        Ready internal {
17            Query(query: bytes::Bytes) => Simple [Dirty] <= crate::codec::FrontendMessage::Query(_),
18            BeginExtended(begin_extended) => Building,
19            FunctionCall(function_call: crate::codec::FunctionCall) => FunctionCalling [Dirty] <= crate::codec::FrontendMessage::FunctionCall(_),
20            Reset(reset) => Resetting [Dirty],
21            Terminate(terminate) => Terminated <= crate::codec::FrontendMessage::Terminate,
22        }
23        FunctionCalling external {
24            FunctionResponse(function_response: bytes::Bytes) => AwaitingReady <= crate::codec::BackendMessage::FunctionCallResponse(_),
25            Error(error: crate::codec::DiagnosticResponse) => Draining <= crate::codec::BackendMessage::ErrorResponse(_),
26        }
27        Simple external {
28            Continue(continue_response: crate::codec::BackendMessage) => Simple <= crate::codec::BackendMessage::RowDescription(_)
29                | crate::codec::BackendMessage::DataRow(_)
30                | crate::codec::BackendMessage::CommandComplete(_)
31                | crate::codec::BackendMessage::EmptyQueryResponse,
32            CopyIn(enter_copy_in: crate::codec::CopyResponse) => CopyIn <= crate::codec::BackendMessage::CopyInResponse(_),
33            CopyOut(enter_copy_out: crate::codec::CopyResponse) => CopyOut <= crate::codec::BackendMessage::CopyOutResponse(_),
34            CopyBoth(enter_copy_both: crate::codec::CopyResponse) => CopyBoth <= crate::codec::BackendMessage::CopyBothResponse(_),
35            Ready(ready: crate::codec::TransactionStatus) => Ready <= crate::codec::BackendMessage::ReadyForQuery(_),
36            Error(error: crate::codec::DiagnosticResponse) => Draining <= crate::codec::BackendMessage::ErrorResponse(_),
37        }
38        Building internal {
39            Parse(parse: crate::codec::Parse) => Building [Dirty] <= crate::codec::FrontendMessage::Parse(_),
40            Describe(describe: crate::codec::Describe) => Building <= crate::codec::FrontendMessage::Describe(_),
41            Bind(bind: crate::codec::Bind) => BoundBuilding [Dirty] <= crate::codec::FrontendMessage::Bind(_),
42            Close(close: crate::codec::Close) => Building <= crate::codec::FrontendMessage::Close(_),
43            Flush(flush) => Building <= crate::codec::FrontendMessage::Flush,
44            Sync(sync) => AwaitingReady <= crate::codec::FrontendMessage::Sync,
45        }
46        BoundBuilding internal {
47            Parse(parse: crate::codec::Parse) => BoundBuilding [Dirty] <= crate::codec::FrontendMessage::Parse(_),
48            Describe(describe: crate::codec::Describe) => BoundBuilding <= crate::codec::FrontendMessage::Describe(_),
49            Bind(bind: crate::codec::Bind) => BoundBuilding [Dirty] <= crate::codec::FrontendMessage::Bind(_),
50            Execute(execute: crate::codec::Execute) => BoundBuilding <= crate::codec::FrontendMessage::Execute(_),
51            Close(close: crate::codec::Close) => BoundBuilding <= crate::codec::FrontendMessage::Close(_),
52            Flush(flush) => BoundBuilding <= crate::codec::FrontendMessage::Flush,
53            Sync(sync) => AwaitingReady <= crate::codec::FrontendMessage::Sync,
54        }
55        AwaitingReady external {
56            Continue(continue_response: crate::codec::BackendMessage) => AwaitingReady <= crate::codec::BackendMessage::ParseComplete
57                | crate::codec::BackendMessage::BindComplete
58                | crate::codec::BackendMessage::CloseComplete
59                | crate::codec::BackendMessage::RowDescription(_)
60                | crate::codec::BackendMessage::NoData
61                | crate::codec::BackendMessage::ParameterDescription(_)
62                | crate::codec::BackendMessage::DataRow(_)
63                | crate::codec::BackendMessage::CommandComplete(_)
64                | crate::codec::BackendMessage::PortalSuspended
65                | crate::codec::BackendMessage::EmptyQueryResponse,
66            Ready(ready: crate::codec::TransactionStatus) => Ready <= crate::codec::BackendMessage::ReadyForQuery(_),
67            Error(error: crate::codec::DiagnosticResponse) => Draining <= crate::codec::BackendMessage::ErrorResponse(_),
68        }
69        CopyIn mixed {
70            internal CopyData(copy_data: bytes::Bytes) => CopyIn <= crate::codec::FrontendMessage::CopyData(_),
71            internal CopyDone(copy_done) => AwaitingReady <= crate::codec::FrontendMessage::CopyDone,
72            internal CopyFail(copy_fail: bytes::Bytes) => AwaitingReady <= crate::codec::FrontendMessage::CopyFail(_),
73            external Error(error: crate::codec::DiagnosticResponse) => Draining <= crate::codec::BackendMessage::ErrorResponse(_),
74        }
75        CopyOut external {
76            CopyData(copy_data: bytes::Bytes) => CopyOut <= crate::codec::BackendMessage::CopyData(_),
77            CopyDone(copy_done) => AwaitingReady <= crate::codec::BackendMessage::CopyDone,
78            Error(error: crate::codec::DiagnosticResponse) => Draining <= crate::codec::BackendMessage::ErrorResponse(_),
79        }
80        CopyBoth mixed {
81            internal SendCopyData(send_copy_data: bytes::Bytes) => CopyBoth <= crate::codec::FrontendMessage::CopyData(_),
82            external ReceiveCopyData(receive_copy_data: bytes::Bytes) => CopyBoth <= crate::codec::BackendMessage::CopyData(_),
83            internal SendCopyDone(send_copy_done) => CopyBothClientDone <= crate::codec::FrontendMessage::CopyDone,
84            external ReceiveCopyDone(receive_copy_done) => CopyBothServerDone <= crate::codec::BackendMessage::CopyDone,
85            external Error(error: crate::codec::DiagnosticResponse) => Draining <= crate::codec::BackendMessage::ErrorResponse(_),
86        }
87        CopyBothClientDone external {
88            ReceiveCopyData(receive_copy_data: bytes::Bytes) => CopyBothClientDone <= crate::codec::BackendMessage::CopyData(_),
89            ReceiveCopyDone(receive_copy_done) => AwaitingReady <= crate::codec::BackendMessage::CopyDone,
90            Error(error: crate::codec::DiagnosticResponse) => Draining <= crate::codec::BackendMessage::ErrorResponse(_),
91        }
92        CopyBothServerDone internal {
93            SendCopyData(send_copy_data: bytes::Bytes) => CopyBothServerDone <= crate::codec::FrontendMessage::CopyData(_),
94            SendCopyDone(send_copy_done) => AwaitingReady <= crate::codec::FrontendMessage::CopyDone,
95        }
96        Draining external {
97            Continue(continue_response: crate::codec::BackendMessage) => Draining <= crate::codec::BackendMessage::RowDescription(_)
98                | crate::codec::BackendMessage::DataRow(_)
99                | crate::codec::BackendMessage::CommandComplete(_)
100                | crate::codec::BackendMessage::EmptyQueryResponse
101                | crate::codec::BackendMessage::ParseComplete
102                | crate::codec::BackendMessage::BindComplete
103                | crate::codec::BackendMessage::CloseComplete
104                | crate::codec::BackendMessage::NoData
105                | crate::codec::BackendMessage::ParameterDescription(_)
106                | crate::codec::BackendMessage::PortalSuspended,
107            Ready(ready: crate::codec::TransactionStatus) => Ready <= crate::codec::BackendMessage::ReadyForQuery(_),
108        }
109        Resetting external {
110            Continue(continue_reset: crate::codec::BackendMessage) => Resetting <= crate::codec::BackendMessage::RowDescription(_)
111                | crate::codec::BackendMessage::DataRow(_)
112                | crate::codec::BackendMessage::EmptyQueryResponse,
113            DiscardComplete(discard_complete: bytes::Bytes) => ResetComplete <= crate::codec::BackendMessage::CommandComplete(_),
114            Error(error: crate::codec::DiagnosticResponse) => Draining <= crate::codec::BackendMessage::ErrorResponse(_),
115        }
116        ResetComplete external {
117            Continue(continue_reset: crate::codec::BackendMessage) => ResetComplete <= crate::codec::BackendMessage::RowDescription(_)
118                | crate::codec::BackendMessage::DataRow(_)
119                | crate::codec::BackendMessage::CommandComplete(_)
120                | crate::codec::BackendMessage::EmptyQueryResponse,
121            ReadyClean(ready_clean: crate::codec::TransactionStatus) => Ready [Pristine] <= crate::codec::BackendMessage::ReadyForQuery(crate::codec::TransactionStatus::Idle),
122            ReadyDirty(ready_dirty: crate::codec::TransactionStatus) => Ready [Dirty] <= crate::codec::BackendMessage::ReadyForQuery(crate::codec::TransactionStatus::InTransaction | crate::codec::TransactionStatus::FailedTransaction),
123            Error(error: crate::codec::DiagnosticResponse) => Draining <= crate::codec::BackendMessage::ErrorResponse(_),
124        }
125        Terminated external {}
126    }
127}
128
129protocol! {
130    pub mod pre_startup {
131        initial PreStartup;
132        messages {
133            internal: crate::pre_startup::PreStartupMessage,
134            external: crate::pre_startup::EncryptionReply,
135        }
136        PreStartup internal {
137            SslRequest(ssl_request) => AwaitingSslReply <= crate::pre_startup::PreStartupMessage::SslRequest,
138            GssRequest(gss_request) => AwaitingGssReply <= crate::pre_startup::PreStartupMessage::GssEncRequest,
139            Cancel(cancel: (u32, bytes::Bytes)) => Terminated <= crate::pre_startup::PreStartupMessage::CancelRequest { .. },
140            Startup(startup: crate::startup::StartupMessage) => Auth <= crate::pre_startup::PreStartupMessage::Startup(_),
141        }
142        AwaitingSslReply external {
143            Accept(accept) => TlsHandshake <= crate::pre_startup::EncryptionReply::Accepted,
144            Reject(reject) => PreStartup <= crate::pre_startup::EncryptionReply::Rejected,
145            LegacyError(legacy_error) => Terminated <= crate::pre_startup::EncryptionReply::LegacyError,
146        }
147        AwaitingGssReply external {
148            Accept(accept) => GssHandshake <= crate::pre_startup::EncryptionReply::Accepted,
149            Reject(reject) => PreStartup <= crate::pre_startup::EncryptionReply::Rejected,
150            LegacyError(legacy_error) => Terminated <= crate::pre_startup::EncryptionReply::LegacyError,
151        }
152        TlsHandshake internal {
153            HandshakeComplete(complete) => PreStartup,
154        }
155        GssHandshake internal {
156            HandshakeComplete(complete) => PreStartup,
157        }
158        Auth external {}
159        Terminated external {}
160    }
161}
162
163protocol! {
164    pub mod server_pre_startup {
165        initial PreStartup;
166        messages {
167            internal: crate::pre_startup::EncryptionReply,
168            external: crate::pre_startup::PreStartupMessage,
169        }
170        PreStartup external {
171            SslRequest(ssl_request) => SslDecision <= crate::pre_startup::PreStartupMessage::SslRequest,
172            GssRequest(gss_request) => GssDecision <= crate::pre_startup::PreStartupMessage::GssEncRequest,
173            Cancel(cancel: (u32, bytes::Bytes)) => Terminated <= crate::pre_startup::PreStartupMessage::CancelRequest { .. },
174            Startup(startup: crate::startup::StartupMessage) => Auth <= crate::pre_startup::PreStartupMessage::Startup(_),
175        }
176        SslDecision internal {
177            Accept(accept) => TlsHandshake <= crate::pre_startup::EncryptionReply::Accepted,
178            Reject(reject) => PreStartup <= crate::pre_startup::EncryptionReply::Rejected,
179            LegacyError(legacy_error) => Terminated <= crate::pre_startup::EncryptionReply::LegacyError,
180        }
181        GssDecision internal {
182            Accept(accept) => GssHandshake <= crate::pre_startup::EncryptionReply::Accepted,
183            Reject(reject) => PreStartup <= crate::pre_startup::EncryptionReply::Rejected,
184            LegacyError(legacy_error) => Terminated <= crate::pre_startup::EncryptionReply::LegacyError,
185        }
186        TlsHandshake internal {
187            HandshakeComplete(complete) => PreStartup,
188        }
189        GssHandshake internal {
190            HandshakeComplete(complete) => PreStartup,
191        }
192        Auth internal {}
193        Terminated internal {}
194    }
195}
196
197protocol! {
198    pub mod authentication {
199        initial Auth;
200        messages {
201            internal: crate::codec::FrontendMessage,
202            external: crate::codec::BackendMessage,
203        }
204        Auth external {
205            Negotiate(negotiate: crate::codec::NegotiateProtocolVersion) => Auth <= crate::codec::BackendMessage::NegotiateProtocolVersion(_),
206            Ok(ok) => AwaitingStartupReady <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::Ok),
207            Cleartext(cleartext) => PasswordResponse <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::CleartextPassword),
208            Md5(md5: [u8; 4]) => PasswordResponse <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::Md5Password { .. }),
209            Sasl(sasl: Vec<bytes::Bytes>) => SaslInitial <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::Sasl { .. }),
210            Gss(gss) => TokenResponse <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::Gss),
211            Sspi(sspi) => TokenResponse <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::Sspi),
212            KerberosV5(kerberos_v5) => TokenResponse <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::KerberosV5),
213            Error(error: crate::codec::DiagnosticResponse) => Terminated <= crate::codec::BackendMessage::ErrorResponse(_),
214        }
215        PasswordResponse internal {
216            Password(password: bytes::Bytes) => AwaitingAuthOk <= crate::codec::FrontendMessage::PasswordResponse(_),
217        }
218        TokenResponse internal {
219            Response(response: bytes::Bytes) => TokenChallenge <= crate::codec::FrontendMessage::PasswordResponse(_),
220        }
221        TokenChallenge external {
222            Continue(continue_token: bytes::Bytes) => TokenResponse <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::GssContinue(_)),
223            Ok(ok) => AwaitingStartupReady <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::Ok),
224            Error(error: crate::codec::DiagnosticResponse) => Terminated <= crate::codec::BackendMessage::ErrorResponse(_),
225        }
226        SaslInitial internal {
227            Initial(initial: crate::server_auth::SaslInitialResponse) => Sasl <= crate::codec::FrontendMessage::PasswordResponse(_),
228        }
229        Sasl external {
230            Continue(continue_response: bytes::Bytes) => SaslChallenge <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::SaslContinue(_)),
231            Final(final_response: bytes::Bytes) => SaslFinal <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::SaslFinal(_)),
232            Error(error: crate::codec::DiagnosticResponse) => Terminated <= crate::codec::BackendMessage::ErrorResponse(_),
233        }
234        SaslChallenge internal {
235            Response(response: bytes::Bytes) => Sasl <= crate::codec::FrontendMessage::PasswordResponse(_),
236        }
237        SaslFinal internal {
238            Verified(verified) => AwaitingAuthOk,
239        }
240        AwaitingAuthOk external {
241            Ok(ok) => AwaitingStartupReady <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::Ok),
242            Error(error: crate::codec::DiagnosticResponse) => Terminated <= crate::codec::BackendMessage::ErrorResponse(_),
243        }
244        AwaitingStartupReady external {
245            Ready(ready: crate::codec::TransactionStatus) => Ready <= crate::codec::BackendMessage::ReadyForQuery(_),
246        }
247        Ready external {}
248        Terminated external {}
249    }
250}
251
252protocol! {
253    pub mod backend {
254        initial Ready;
255        messages {
256            internal: crate::codec::BackendMessage,
257            external: crate::codec::FrontendMessage,
258        }
259        Ready external {
260            Query(query: bytes::Bytes) => Simple [Dirty] <= crate::codec::FrontendMessage::Query(_),
261            Parse(parse: crate::codec::Parse) => ParseResponse [Dirty] <= crate::codec::FrontendMessage::Parse(_),
262            Bind(bind: crate::codec::Bind) => BindResponse [Dirty] <= crate::codec::FrontendMessage::Bind(_),
263            Describe(describe: crate::codec::Describe) => DescribeResponse <= crate::codec::FrontendMessage::Describe(_),
264            Execute(execute: crate::codec::Execute) => ExecuteResponse [Dirty] <= crate::codec::FrontendMessage::Execute(_),
265            Close(close: crate::codec::Close) => CloseResponse <= crate::codec::FrontendMessage::Close(_),
266            FunctionCall(function_call: crate::codec::FunctionCall) => FunctionResponse [Dirty] <= crate::codec::FrontendMessage::FunctionCall(_),
267            Terminate(terminate) => Terminated <= crate::codec::FrontendMessage::Terminate,
268        }
269        Simple internal {
270            Continue(continue_response: crate::codec::BackendMessage) => Simple <= crate::codec::BackendMessage::RowDescription(_)
271                | crate::codec::BackendMessage::DataRow(_)
272                | crate::codec::BackendMessage::CommandComplete(_)
273                | crate::codec::BackendMessage::EmptyQueryResponse,
274            CopyIn(copy_in: crate::codec::CopyResponse) => SimpleCopyIn <= crate::codec::BackendMessage::CopyInResponse(_),
275            CopyOut(copy_out: crate::codec::CopyResponse) => SimpleCopyOut <= crate::codec::BackendMessage::CopyOutResponse(_),
276            CopyBoth(copy_both: crate::codec::CopyResponse) => SimpleCopyBoth <= crate::codec::BackendMessage::CopyBothResponse(_),
277            Ready(ready: crate::codec::TransactionStatus) => Ready <= crate::codec::BackendMessage::ReadyForQuery(_),
278            Error(error: crate::codec::DiagnosticResponse) => SimpleError <= crate::codec::BackendMessage::ErrorResponse(_),
279        }
280        SimpleError internal {
281            Ready(ready: crate::codec::TransactionStatus) => Ready <= crate::codec::BackendMessage::ReadyForQuery(_),
282        }
283        Building external {
284            Parse(parse: crate::codec::Parse) => ParseResponse [Dirty] <= crate::codec::FrontendMessage::Parse(_),
285            Bind(bind: crate::codec::Bind) => BindResponse [Dirty] <= crate::codec::FrontendMessage::Bind(_),
286            Describe(describe: crate::codec::Describe) => DescribeResponse <= crate::codec::FrontendMessage::Describe(_),
287            Execute(execute: crate::codec::Execute) => ExecuteResponse [Dirty] <= crate::codec::FrontendMessage::Execute(_),
288            Close(close: crate::codec::Close) => CloseResponse <= crate::codec::FrontendMessage::Close(_),
289            Flush(flush) => Building <= crate::codec::FrontendMessage::Flush,
290            Sync(sync) => SyncResponse <= crate::codec::FrontendMessage::Sync,
291        }
292        ParseResponse internal {
293            Complete(complete) => Building <= crate::codec::BackendMessage::ParseComplete,
294            Error(error: crate::codec::DiagnosticResponse) => ExtendedError <= crate::codec::BackendMessage::ErrorResponse(_),
295        }
296        BindResponse internal {
297            Complete(complete) => Building <= crate::codec::BackendMessage::BindComplete,
298            Error(error: crate::codec::DiagnosticResponse) => ExtendedError <= crate::codec::BackendMessage::ErrorResponse(_),
299        }
300        DescribeResponse internal {
301            RowDescription(row_description: crate::codec::RowDescription) => Building <= crate::codec::BackendMessage::RowDescription(_),
302            NoData(no_data) => Building <= crate::codec::BackendMessage::NoData,
303            Error(error: crate::codec::DiagnosticResponse) => ExtendedError <= crate::codec::BackendMessage::ErrorResponse(_),
304        }
305        ExecuteResponse internal {
306            Continue(continue_response: crate::codec::BackendMessage) => ExecuteResponse <= crate::codec::BackendMessage::RowDescription(_)
307                | crate::codec::BackendMessage::DataRow(_)
308                | crate::codec::BackendMessage::EmptyQueryResponse,
309            CopyIn(copy_in: crate::codec::CopyResponse) => ExtendedCopyIn <= crate::codec::BackendMessage::CopyInResponse(_),
310            CopyOut(copy_out: crate::codec::CopyResponse) => ExtendedCopyOut <= crate::codec::BackendMessage::CopyOutResponse(_),
311            CopyBoth(copy_both: crate::codec::CopyResponse) => ExtendedCopyBoth <= crate::codec::BackendMessage::CopyBothResponse(_),
312            CommandComplete(command_complete: bytes::Bytes) => Building <= crate::codec::BackendMessage::CommandComplete(_),
313            PortalSuspended(portal_suspended) => Building <= crate::codec::BackendMessage::PortalSuspended,
314            Error(error: crate::codec::DiagnosticResponse) => ExtendedError <= crate::codec::BackendMessage::ErrorResponse(_),
315        }
316        CloseResponse internal {
317            Complete(complete) => Building <= crate::codec::BackendMessage::CloseComplete,
318            Error(error: crate::codec::DiagnosticResponse) => ExtendedError <= crate::codec::BackendMessage::ErrorResponse(_),
319        }
320        ExtendedError external {
321            Discard(discard) => ExtendedError <= crate::codec::FrontendMessage::Parse(_)
322                | crate::codec::FrontendMessage::Bind(_)
323                | crate::codec::FrontendMessage::Describe(_)
324                | crate::codec::FrontendMessage::Execute(_)
325                | crate::codec::FrontendMessage::Close(_)
326                | crate::codec::FrontendMessage::Flush
327                | crate::codec::FrontendMessage::Query(_)
328                | crate::codec::FrontendMessage::FunctionCall(_)
329                | crate::codec::FrontendMessage::Terminate
330                | crate::codec::FrontendMessage::CopyData(_)
331                | crate::codec::FrontendMessage::CopyDone
332                | crate::codec::FrontendMessage::CopyFail(_)
333                | crate::codec::FrontendMessage::PasswordResponse(_),
334            Sync(sync) => SyncResponse <= crate::codec::FrontendMessage::Sync,
335        }
336        SyncResponse internal {
337            Ready(ready: crate::codec::TransactionStatus) => Ready <= crate::codec::BackendMessage::ReadyForQuery(_),
338        }
339        FunctionResponse internal {
340            Result(result: bytes::Bytes) => FunctionReady <= crate::codec::BackendMessage::FunctionCallResponse(_),
341            Error(error: crate::codec::DiagnosticResponse) => FunctionReady <= crate::codec::BackendMessage::ErrorResponse(_),
342        }
343        FunctionReady internal {
344            Ready(ready: crate::codec::TransactionStatus) => Ready <= crate::codec::BackendMessage::ReadyForQuery(_),
345        }
346        SimpleCopyIn external {
347            Data(data: bytes::Bytes) => SimpleCopyIn <= crate::codec::FrontendMessage::CopyData(_),
348            Done(done) => SimpleCopyInDone <= crate::codec::FrontendMessage::CopyDone,
349            Fail(fail: bytes::Bytes) => SimpleCopyInFailed <= crate::codec::FrontendMessage::CopyFail(_),
350        }
351        SimpleCopyInDone internal {
352            CommandComplete(command_complete: bytes::Bytes) => SimpleCopyReady <= crate::codec::BackendMessage::CommandComplete(_),
353        }
354        SimpleCopyInFailed internal {
355            Error(error: crate::codec::DiagnosticResponse) => SimpleCopyReady <= crate::codec::BackendMessage::ErrorResponse(_),
356        }
357        SimpleCopyOut internal {
358            Data(data: bytes::Bytes) => SimpleCopyOut <= crate::codec::BackendMessage::CopyData(_),
359            Done(done) => SimpleCopyOutDone <= crate::codec::BackendMessage::CopyDone,
360            Error(error: crate::codec::DiagnosticResponse) => SimpleCopyReady <= crate::codec::BackendMessage::ErrorResponse(_),
361        }
362        SimpleCopyOutDone internal {
363            CommandComplete(command_complete: bytes::Bytes) => SimpleCopyReady <= crate::codec::BackendMessage::CommandComplete(_),
364        }
365        SimpleCopyReady internal {
366            Ready(ready: crate::codec::TransactionStatus) => Ready <= crate::codec::BackendMessage::ReadyForQuery(_),
367        }
368        ExtendedCopyIn external {
369            Data(data: bytes::Bytes) => ExtendedCopyIn <= crate::codec::FrontendMessage::CopyData(_),
370            Done(done) => ExtendedCopyInDone <= crate::codec::FrontendMessage::CopyDone,
371            Fail(fail: bytes::Bytes) => ExtendedCopyInFailed <= crate::codec::FrontendMessage::CopyFail(_),
372        }
373        ExtendedCopyInDone internal {
374            CommandComplete(command_complete: bytes::Bytes) => Building <= crate::codec::BackendMessage::CommandComplete(_),
375        }
376        ExtendedCopyInFailed internal {
377            Error(error: crate::codec::DiagnosticResponse) => ExtendedError <= crate::codec::BackendMessage::ErrorResponse(_),
378        }
379        ExtendedCopyOut internal {
380            Data(data: bytes::Bytes) => ExtendedCopyOut <= crate::codec::BackendMessage::CopyData(_),
381            Done(done) => ExtendedCopyOutDone <= crate::codec::BackendMessage::CopyDone,
382            Error(error: crate::codec::DiagnosticResponse) => ExtendedError <= crate::codec::BackendMessage::ErrorResponse(_),
383        }
384        ExtendedCopyOutDone internal {
385            CommandComplete(command_complete: bytes::Bytes) => Building <= crate::codec::BackendMessage::CommandComplete(_),
386        }
387        SimpleCopyBoth mixed {
388            internal SendData(send_data: bytes::Bytes) => SimpleCopyBoth <= crate::codec::BackendMessage::CopyData(_),
389            external ReceiveData(receive_data: bytes::Bytes) => SimpleCopyBoth <= crate::codec::FrontendMessage::CopyData(_),
390            internal SendDone(send_done) => SimpleCopyBothServerDone <= crate::codec::BackendMessage::CopyDone,
391            external ReceiveDone(receive_done) => SimpleCopyBothClientDone <= crate::codec::FrontendMessage::CopyDone,
392            external Fail(fail: bytes::Bytes) => SimpleCopyBothFailed <= crate::codec::FrontendMessage::CopyFail(_),
393            internal Error(error: crate::codec::DiagnosticResponse) => SimpleCopyReady <= crate::codec::BackendMessage::ErrorResponse(_),
394        }
395        SimpleCopyBothClientDone internal {
396            SendData(send_data: bytes::Bytes) => SimpleCopyBothClientDone <= crate::codec::BackendMessage::CopyData(_),
397            SendDone(send_done) => SimpleCopyBothDone <= crate::codec::BackendMessage::CopyDone,
398            Error(error: crate::codec::DiagnosticResponse) => SimpleCopyReady <= crate::codec::BackendMessage::ErrorResponse(_),
399        }
400        SimpleCopyBothServerDone external {
401            ReceiveData(receive_data: bytes::Bytes) => SimpleCopyBothServerDone <= crate::codec::FrontendMessage::CopyData(_),
402            ReceiveDone(receive_done) => SimpleCopyBothDone <= crate::codec::FrontendMessage::CopyDone,
403            Fail(fail: bytes::Bytes) => SimpleCopyBothFailed <= crate::codec::FrontendMessage::CopyFail(_),
404        }
405        SimpleCopyBothDone internal {
406            CommandComplete(command_complete: bytes::Bytes) => SimpleCopyReady <= crate::codec::BackendMessage::CommandComplete(_),
407        }
408        SimpleCopyBothFailed internal {
409            Error(error: crate::codec::DiagnosticResponse) => SimpleCopyReady <= crate::codec::BackendMessage::ErrorResponse(_),
410        }
411        ExtendedCopyBoth mixed {
412            internal SendData(send_data: bytes::Bytes) => ExtendedCopyBoth <= crate::codec::BackendMessage::CopyData(_),
413            external ReceiveData(receive_data: bytes::Bytes) => ExtendedCopyBoth <= crate::codec::FrontendMessage::CopyData(_),
414            internal SendDone(send_done) => ExtendedCopyBothServerDone <= crate::codec::BackendMessage::CopyDone,
415            external ReceiveDone(receive_done) => ExtendedCopyBothClientDone <= crate::codec::FrontendMessage::CopyDone,
416            external Fail(fail: bytes::Bytes) => ExtendedCopyBothFailed <= crate::codec::FrontendMessage::CopyFail(_),
417            internal Error(error: crate::codec::DiagnosticResponse) => ExtendedError <= crate::codec::BackendMessage::ErrorResponse(_),
418        }
419        ExtendedCopyBothClientDone internal {
420            SendData(send_data: bytes::Bytes) => ExtendedCopyBothClientDone <= crate::codec::BackendMessage::CopyData(_),
421            SendDone(send_done) => ExtendedCopyBothDone <= crate::codec::BackendMessage::CopyDone,
422            Error(error: crate::codec::DiagnosticResponse) => ExtendedError <= crate::codec::BackendMessage::ErrorResponse(_),
423        }
424        ExtendedCopyBothServerDone external {
425            ReceiveData(receive_data: bytes::Bytes) => ExtendedCopyBothServerDone <= crate::codec::FrontendMessage::CopyData(_),
426            ReceiveDone(receive_done) => ExtendedCopyBothDone <= crate::codec::FrontendMessage::CopyDone,
427            Fail(fail: bytes::Bytes) => ExtendedCopyBothFailed <= crate::codec::FrontendMessage::CopyFail(_),
428        }
429        ExtendedCopyBothDone internal {
430            CommandComplete(command_complete: bytes::Bytes) => Building <= crate::codec::BackendMessage::CommandComplete(_),
431        }
432        ExtendedCopyBothFailed internal {
433            Error(error: crate::codec::DiagnosticResponse) => ExtendedError <= crate::codec::BackendMessage::ErrorResponse(_),
434        }
435        Terminated external {}
436    }
437}
438
439protocol! {
440    pub mod server_authentication {
441        initial Startup;
442        messages {
443            internal: crate::codec::BackendMessage,
444            external: crate::codec::FrontendMessage,
445        }
446        Startup internal {
447            Begin(begin) => Auth,
448            Reject(reject) => Terminated,
449        }
450        Auth internal {
451            Negotiate(negotiate: crate::codec::NegotiateProtocolVersion) => Auth <= crate::codec::BackendMessage::NegotiateProtocolVersion(_),
452            Cleartext(cleartext) => PasswordResponse <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::CleartextPassword),
453            Md5(md5: [u8; 4]) => PasswordResponse <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::Md5Password { .. }),
454            Sasl(sasl: Vec<bytes::Bytes>) => SaslInitial <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::Sasl { .. }),
455            Gss(gss) => TokenResponse <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::Gss),
456            Sspi(sspi) => TokenResponse <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::Sspi),
457            KerberosV5(kerberos_v5) => TokenResponse <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::KerberosV5),
458            Ok(ok) => StartupReady <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::Ok),
459            Error(error: crate::codec::DiagnosticResponse) => Terminated <= crate::codec::BackendMessage::ErrorResponse(_),
460        }
461        PasswordResponse external {
462            Response(response: bytes::Bytes) => Auth <= crate::codec::FrontendMessage::PasswordResponse(_),
463        }
464        SaslInitial external {
465            Initial(initial: crate::server_auth::SaslInitialResponse) => Sasl <= crate::codec::FrontendMessage::PasswordResponse(_),
466        }
467        Sasl internal {
468            Continue(continue_response: bytes::Bytes) => SaslResponse <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::SaslContinue(_)),
469            Final(final_response: bytes::Bytes) => Auth <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::SaslFinal(_)),
470            Error(error: crate::codec::DiagnosticResponse) => Terminated <= crate::codec::BackendMessage::ErrorResponse(_),
471        }
472        SaslResponse external {
473            Response(response: bytes::Bytes) => Sasl <= crate::codec::FrontendMessage::PasswordResponse(_),
474        }
475        TokenResponse external {
476            Response(response: bytes::Bytes) => TokenPolicy <= crate::codec::FrontendMessage::PasswordResponse(_),
477        }
478        TokenPolicy internal {
479            Continue(continue_token: bytes::Bytes) => TokenResponse <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::GssContinue(_)),
480            Verified(verified) => Auth,
481            Error(error: crate::codec::DiagnosticResponse) => Terminated <= crate::codec::BackendMessage::ErrorResponse(_),
482        }
483        StartupReady internal {
484            ParameterStatus(parameter_status: (bytes::Bytes, bytes::Bytes)) => StartupReady <= crate::codec::BackendMessage::ParameterStatus { .. },
485            BackendKeyData(backend_key_data: (u32, bytes::Bytes)) => StartupReady <= crate::codec::BackendMessage::BackendKeyData { .. },
486            Ready(ready: crate::codec::TransactionStatus) => Ready <= crate::codec::BackendMessage::ReadyForQuery(_),
487        }
488        Ready external {}
489        Terminated external {}
490    }
491}
492
493#[cfg(test)]
494mod tests {
495    use std::collections::BTreeMap;
496
497    use bytes::Bytes;
498
499    use super::{
500        authentication, backend, frontend, pre_startup, server_authentication, server_pre_startup,
501    };
502    use crate::{
503        Conn,
504        auth::AuthOffer,
505        codec::{
506            Authentication, BackendMessage, Bind, Execute, FrontendMessage, Parse,
507            TransactionStatus,
508        },
509        demux::SessionItem,
510        session::{AwaitingReadyTransition, ReadyState},
511        startup::{ProtocolVersion, StartupMessage},
512    };
513    use frontend::{Event, RuntimeFsm, RuntimeState, Session};
514
515    #[test]
516    fn railroad_labels_use_variant_syntax_and_link_payload_types() {
517        let svg = frontend::FRONTEND_RAILROAD_SVG;
518        assert!(svg.contains("◁ ReceiveCopyData(</tspan>"));
519        assert!(svg.contains("bytes::Bytes</tspan>"));
520        assert!(svg.contains(")</tspan>"));
521        assert!(svg.contains("xlink:href=\"https://docs.rs/bytes/1/bytes/struct.Bytes.html\""));
522        assert!(svg.contains("class=\"link\""));
523        assert!(svg.contains("▷ Query(</tspan>"));
524        assert!(svg.contains(") [Dirty]</tspan>"));
525        assert!(!svg.contains("class=\"link\"> <g class=\"terminal\""));
526        assert!(!svg.contains("&amp; ReceiveCopyData"));
527    }
528
529    macro_rules! exhaust_generated_runtime {
530        ($module:ident, $depth:expr) => {{
531            fn visit(runtime: $module::RuntimeFsm, depth: usize) {
532                for &event in $module::ALL_EVENTS {
533                    let state = runtime.state();
534                    let mut next = runtime;
535                    match next.step(event) {
536                        Ok(()) if depth > 0 => visit(next, depth - 1),
537                        Ok(()) => {}
538                        Err(error) => {
539                            assert_eq!(error.state, state);
540                            assert_eq!(error.event, event);
541                            assert_eq!(next.state(), state);
542                        }
543                    }
544                }
545            }
546            visit($module::RuntimeFsm::new(), $depth);
547        }};
548    }
549
550    #[test]
551    fn every_generated_protocol_exhausts_reachable_valid_and_invalid_events() {
552        exhaust_generated_runtime!(frontend, 5);
553        exhaust_generated_runtime!(backend, 5);
554        exhaust_generated_runtime!(pre_startup, 5);
555        exhaust_generated_runtime!(server_pre_startup, 5);
556        exhaust_generated_runtime!(authentication, 6);
557        exhaust_generated_runtime!(server_authentication, 6);
558    }
559
560    #[test]
561    fn generated_backend_projection_is_state_aware() {
562        let parse = FrontendMessage::Parse(Parse {
563            statement: Bytes::from_static(b"statement"),
564            query: Bytes::from_static(b"select 1"),
565            parameter_types: Vec::new(),
566        });
567        assert_eq!(
568            backend::project_external(backend::RuntimeState::Ready, &parse),
569            Some(backend::Event::Parse)
570        );
571        assert_eq!(
572            backend::project_external(backend::RuntimeState::SimpleCopyIn, &parse),
573            None
574        );
575        assert_eq!(
576            backend::project_external(backend::RuntimeState::ExtendedError, &FrontendMessage::Sync,),
577            Some(backend::Event::Sync)
578        );
579        assert_eq!(
580            backend::project_external(
581                backend::RuntimeState::ExtendedError,
582                &FrontendMessage::Flush,
583            ),
584            Some(backend::Event::Discard)
585        );
586        assert_eq!(
587            backend::project_internal(
588                backend::RuntimeState::Simple,
589                &BackendMessage::ReadyForQuery(TransactionStatus::Idle),
590            ),
591            Some(backend::Event::Ready)
592        );
593        assert_eq!(
594            backend::project_internal(
595                backend::RuntimeState::ExtendedCopyOut,
596                &BackendMessage::CopyDone,
597            ),
598            Some(backend::Event::Done)
599        );
600    }
601
602    #[test]
603    fn generated_frontend_projection_covers_wire_messages_only() {
604        assert_eq!(
605            frontend::project_internal(frontend::RuntimeState::Building, &FrontendMessage::Sync,),
606            Some(frontend::Event::Sync)
607        );
608        assert_eq!(
609            frontend::project_external(
610                frontend::RuntimeState::Simple,
611                &BackendMessage::ReadyForQuery(TransactionStatus::Idle),
612            ),
613            Some(frontend::Event::Ready)
614        );
615        assert_eq!(
616            frontend::project_external(
617                frontend::RuntimeState::ResetComplete,
618                &BackendMessage::ReadyForQuery(TransactionStatus::FailedTransaction),
619            ),
620            Some(frontend::Event::ReadyDirty)
621        );
622        assert_eq!(
623            frontend::project_external(
624                frontend::RuntimeState::CopyIn,
625                &BackendMessage::CopyData(Bytes::from_static(b"illegal direction")),
626            ),
627            None
628        );
629    }
630
631    #[test]
632    fn codec_messages_drive_generated_extended_and_copy_sequences() {
633        let parse = FrontendMessage::Parse(Parse {
634            statement: Bytes::new(),
635            query: Bytes::from_static(b"select $1"),
636            parameter_types: vec![23],
637        });
638        let mut extended = backend::RuntimeFsm::new();
639        extended
640            .step_projected(&parse, backend::project_external)
641            .unwrap();
642        extended
643            .step_projected(&BackendMessage::ParseComplete, backend::project_internal)
644            .unwrap();
645        extended
646            .step_projected(&FrontendMessage::Sync, backend::project_external)
647            .unwrap();
648        extended
649            .step_projected(
650                &BackendMessage::ReadyForQuery(TransactionStatus::Idle),
651                backend::project_internal,
652            )
653            .unwrap();
654        assert_eq!(extended.state(), backend::RuntimeState::Ready);
655
656        let mut copy = backend::RuntimeFsm::new();
657        copy.step_projected(
658            &FrontendMessage::Query(Bytes::from_static(b"copy t from stdin")),
659            backend::project_external,
660        )
661        .unwrap();
662        copy.step_projected(
663            &BackendMessage::CopyInResponse(crate::codec::CopyResponse {
664                overall_format: 0,
665                column_formats: vec![0],
666            }),
667            backend::project_internal,
668        )
669        .unwrap();
670        assert_eq!(copy.state(), backend::RuntimeState::SimpleCopyIn);
671        assert!(
672            copy.step_projected(
673                &FrontendMessage::Query(Bytes::from_static(b"select 1")),
674                backend::project_external,
675            )
676            .is_err()
677        );
678        assert_eq!(copy.state(), backend::RuntimeState::SimpleCopyIn);
679    }
680
681    #[test]
682    fn generated_typestate_and_runtime_accept_the_extended_loop() {
683        let _typed = Session::new()
684            .begin_extended()
685            .parse()
686            .bind()
687            .execute()
688            .sync()
689            .ready();
690
691        let mut runtime = RuntimeFsm::new();
692        for event in [
693            Event::BeginExtended,
694            Event::Parse,
695            Event::Bind,
696            Event::Execute,
697            Event::Sync,
698            Event::Ready,
699        ] {
700            runtime.step(event).unwrap();
701        }
702        assert_eq!(runtime.state(), RuntimeState::Ready);
703    }
704
705    #[test]
706    fn generated_backend_discards_failed_pipeline_until_sync() {
707        let _typed = backend::Session::new()
708            .parse()
709            .error()
710            .discard()
711            .discard()
712            .sync()
713            .ready()
714            .terminate();
715
716        let mut runtime = backend::RuntimeFsm::new();
717        for event in [
718            backend::Event::Parse,
719            backend::Event::Error,
720            backend::Event::Discard,
721            backend::Event::Discard,
722            backend::Event::Sync,
723            backend::Event::Ready,
724            backend::Event::Terminate,
725        ] {
726            runtime.step(event).unwrap();
727        }
728        assert_eq!(runtime.state(), backend::RuntimeState::Terminated);
729    }
730
731    #[test]
732    fn generated_backend_copy_resumes_its_enclosing_session() {
733        let _simple = backend::Session::new()
734            .query()
735            .copy_in()
736            .data()
737            .done()
738            .command_complete()
739            .ready();
740        let _extended = backend::Session::new()
741            .execute()
742            .copy_out()
743            .data()
744            .done()
745            .command_complete()
746            .sync()
747            .ready();
748
749        let mut runtime = backend::RuntimeFsm::new();
750        for event in [
751            backend::Event::Execute,
752            backend::Event::CopyOut,
753            backend::Event::Data,
754            backend::Event::Done,
755            backend::Event::CommandComplete,
756        ] {
757            runtime.step(event).unwrap();
758        }
759        assert_eq!(runtime.state(), backend::RuntimeState::Building);
760        runtime.step(backend::Event::Sync).unwrap();
761        runtime.step(backend::Event::Ready).unwrap();
762        assert_eq!(runtime.state(), backend::RuntimeState::Ready);
763    }
764
765    #[test]
766    fn generated_backend_copy_both_tracks_independent_half_closes() {
767        let _server_first = backend::Session::new()
768            .query()
769            .copy_both()
770            .send_data()
771            .receive_data()
772            .send_done()
773            .receive_data()
774            .receive_done()
775            .command_complete()
776            .ready();
777        let _client_first = backend::Session::new()
778            .execute()
779            .copy_both()
780            .receive_done()
781            .send_data()
782            .send_done()
783            .command_complete()
784            .sync()
785            .ready();
786
787        let mut runtime = backend::RuntimeFsm::new();
788        runtime.step(backend::Event::Query).unwrap();
789        runtime.step(backend::Event::CopyBoth).unwrap();
790        assert_eq!(runtime.choice(), backend::ChoiceKind::Mixed);
791        assert_eq!(
792            runtime.event_choice(backend::Event::SendData),
793            Some(backend::ChoiceKind::Internal)
794        );
795        assert_eq!(
796            runtime.event_choice(backend::Event::ReceiveData),
797            Some(backend::ChoiceKind::External)
798        );
799        runtime.step(backend::Event::SendDone).unwrap();
800        assert!(runtime.step(backend::Event::SendData).is_err());
801        runtime.step(backend::Event::ReceiveDone).unwrap();
802        runtime.step(backend::Event::CommandComplete).unwrap();
803        runtime.step(backend::Event::Ready).unwrap();
804        assert_eq!(runtime.state(), backend::RuntimeState::Ready);
805    }
806
807    #[test]
808    fn generated_runtime_rejects_query_during_copy() {
809        let mut runtime = RuntimeFsm::new();
810        runtime.step(Event::Query).unwrap();
811        runtime.step(Event::CopyIn).unwrap();
812        assert!(runtime.step(Event::Query).is_err());
813        assert_eq!(
814            runtime.event_choice(Event::Error),
815            Some(frontend::ChoiceKind::External)
816        );
817        runtime.step(Event::Error).unwrap();
818        assert_eq!(runtime.state(), RuntimeState::Draining);
819    }
820
821    #[test]
822    fn generated_copy_both_waits_for_both_half_closes() {
823        let mut directions = RuntimeFsm::new();
824        directions.step(Event::Query).unwrap();
825        directions.step(Event::CopyBoth).unwrap();
826        assert_eq!(directions.choice(), frontend::ChoiceKind::Mixed);
827        assert_eq!(
828            directions.event_choice(Event::SendCopyData),
829            Some(frontend::ChoiceKind::Internal)
830        );
831        assert_eq!(
832            directions.event_choice(Event::ReceiveCopyData),
833            Some(frontend::ChoiceKind::External)
834        );
835
836        let mut client_first = RuntimeFsm::new();
837        for event in [
838            Event::Query,
839            Event::CopyBoth,
840            Event::SendCopyDone,
841            Event::ReceiveCopyData,
842            Event::ReceiveCopyDone,
843        ] {
844            client_first.step(event).unwrap();
845        }
846        assert_eq!(client_first.state(), RuntimeState::AwaitingReady);
847
848        let mut server_first = RuntimeFsm::new();
849        for event in [
850            Event::Query,
851            Event::CopyBoth,
852            Event::ReceiveCopyDone,
853            Event::SendCopyData,
854            Event::SendCopyDone,
855        ] {
856            server_first.step(event).unwrap();
857        }
858        assert_eq!(server_first.state(), RuntimeState::AwaitingReady);
859    }
860
861    #[test]
862    fn generated_function_call_and_termination_match_typed_paths() {
863        let _function = Session::new()
864            .function_call()
865            .function_response()
866            .ready()
867            .terminate();
868
869        let mut runtime = RuntimeFsm::new();
870        for event in [
871            Event::FunctionCall,
872            Event::FunctionResponse,
873            Event::Ready,
874            Event::Terminate,
875        ] {
876            runtime.step(event).unwrap();
877        }
878        assert_eq!(runtime.state(), RuntimeState::Terminated);
879    }
880
881    #[test]
882    fn generated_pool_reset_requires_discard_and_ready_evidence() {
883        let _typed = Session::new()
884            .reset()
885            .continue_reset()
886            .discard_complete()
887            .continue_reset()
888            .ready_clean();
889
890        let mut runtime = RuntimeFsm::new();
891        runtime.step(Event::Reset).unwrap();
892        assert!(runtime.step(Event::ReadyClean).is_err());
893        runtime.step(Event::DiscardComplete).unwrap();
894        runtime.step(Event::ReadyClean).unwrap();
895        assert_eq!(runtime.state(), RuntimeState::Ready);
896
897        let dirty: Conn<(), crate::auth::Ready, crate::Dirty> =
898            Conn::new(()).transition().mark_dirty();
899        let (resetting, _) = dirty.begin_reset().unwrap();
900        let crate::session::ResettingTransition::Complete(complete) =
901            resetting.offer(SessionItem::CommandComplete {
902                tag: Bytes::from_static(b"DISCARD ALL"),
903                command: crate::demux::CommandIndex(1),
904                notices: Vec::new(),
905            })
906        else {
907            panic!("DISCARD ALL did not advance reset recovery")
908        };
909        let crate::session::ResetCompleteTransition::Ready(ready) =
910            complete.offer(SessionItem::ReadyForQuery {
911                status: TransactionStatus::Idle,
912                parameters_changed: false,
913            })
914        else {
915            panic!("idle readiness did not restore pristine evidence")
916        };
917        ready.release();
918    }
919
920    #[test]
921    fn generated_transport_session_tracks_cleanliness_effects() {
922        #[derive(Debug)]
923        struct InitiallyClean;
924
925        let ready: frontend::TypedSession<(), frontend::Ready, InitiallyClean> =
926            frontend::TypedSession::with_transport(());
927        let (dirty, query): (
928            frontend::TypedSession<(), frontend::Simple, frontend::Dirty>,
929            Bytes,
930        ) = ready
931            .query(Bytes::from_static(b"select 1"), |(), query| {
932                Ok::<_, std::convert::Infallible>(query)
933            })
934            .expect("query handler is infallible");
935        assert_eq!(query, Bytes::from_static(b"select 1"));
936        let (dirty, _status): (
937            frontend::TypedSession<(), frontend::Ready, frontend::Dirty>,
938            TransactionStatus,
939        ) = dirty
940            .ready(TransactionStatus::Idle, |(), status| {
941                Ok::<_, std::convert::Infallible>(status)
942            })
943            .expect("readiness handler is infallible");
944        assert_eq!(dirty.into_transport(), ());
945
946        let dirty: frontend::TypedSession<(), frontend::Ready, frontend::Dirty> =
947            frontend::TypedSession::with_transport(());
948        let (reset_complete, _tag) = dirty
949            .reset()
950            .discard_complete(Bytes::from_static(b"DISCARD ALL"), |(), tag| {
951                Ok::<_, std::convert::Infallible>(tag)
952            })
953            .expect("command handler is infallible");
954        let (clean, _status): (
955            frontend::TypedSession<(), frontend::Ready, frontend::Pristine>,
956            TransactionStatus,
957        ) = reset_complete
958            .ready_clean(TransactionStatus::Idle, |(), status| {
959                Ok::<_, std::convert::Infallible>(status)
960            })
961            .expect("readiness handler is infallible");
962        assert_eq!(clean.into_transport(), ());
963    }
964
965    #[test]
966    fn generated_frontend_parse_payload_is_inspectable_and_fallible() {
967        #[derive(Debug)]
968        struct Clean;
969
970        let ready: frontend::TypedSession<Vec<crate::codec::Frame>, frontend::Ready, Clean> =
971            frontend::TypedSession::with_transport(Vec::new());
972        let parse = Parse {
973            statement: Bytes::from_static(b"statement"),
974            query: Bytes::from_static(b"select encrypted_column"),
975            parameter_types: vec![23],
976        };
977        let (building, query): (
978            frontend::TypedSession<Vec<crate::codec::Frame>, frontend::Building, frontend::Dirty>,
979            Bytes,
980        ) = ready
981            .begin_extended()
982            .parse(parse, |frames, message| {
983                let query = message.query.clone();
984                frames.push(message.to_frame()?);
985                Ok::<_, std::io::Error>(query)
986            })
987            .unwrap();
988        assert_eq!(query, Bytes::from_static(b"select encrypted_column"));
989        assert_eq!(building.into_transport()[0].tag, b'P');
990
991        let ready: frontend::TypedSession<Vec<crate::codec::Frame>, frontend::Ready, Clean> =
992            frontend::TypedSession::with_transport(Vec::new());
993        let invalid = Parse {
994            statement: Bytes::from_static(b"bad\0statement"),
995            query: Bytes::from_static(b"select 1"),
996            parameter_types: vec![],
997        };
998        let (building, error) = ready
999            .begin_extended()
1000            .parse(invalid, |frames, message| {
1001                frames.push(message.to_frame()?);
1002                Ok::<_, std::io::Error>(())
1003            })
1004            .unwrap_err();
1005        assert_eq!(error.kind(), std::io::ErrorKind::InvalidInput);
1006        assert!(building.into_transport().is_empty());
1007    }
1008
1009    #[test]
1010    fn generated_pre_startup_requires_handshake_before_startup() {
1011        struct Clean;
1012        struct Tcp;
1013        struct Tls(Tcp);
1014
1015        let _typed = pre_startup::Session::new()
1016            .ssl_request()
1017            .accept()
1018            .complete()
1019            .startup();
1020
1021        let mut runtime = pre_startup::RuntimeFsm::new();
1022        runtime.step(pre_startup::Event::SslRequest).unwrap();
1023        assert!(runtime.step(pre_startup::Event::Startup).is_err());
1024        runtime.step(pre_startup::Event::Accept).unwrap();
1025        runtime.step(pre_startup::Event::HandshakeComplete).unwrap();
1026        runtime.step(pre_startup::Event::Startup).unwrap();
1027
1028        let pre_startup: pre_startup::TypedSession<Tcp, pre_startup::PreStartup, Clean> =
1029            pre_startup::TypedSession::with_transport(Tcp);
1030        let startup = crate::startup::StartupMessage {
1031            version: crate::startup::ProtocolVersion::V3_0,
1032            parameters: BTreeMap::new(),
1033        };
1034        let auth = pre_startup
1035            .ssl_request()
1036            .accept()
1037            .map_transport(Tls)
1038            .complete()
1039            .startup(startup, |_, startup| {
1040                Ok::<_, std::convert::Infallible>(startup)
1041            });
1042        let (auth, _startup): (
1043            pre_startup::TypedSession<Tls, pre_startup::Auth, Clean>,
1044            crate::startup::StartupMessage,
1045        ) = match auth {
1046            Ok(success) => success,
1047            Err((_session, never)) => match never {},
1048        };
1049        let Tls(_tcp) = auth.into_transport();
1050
1051        assert_eq!(
1052            pre_startup::project_internal(
1053                pre_startup::RuntimeState::PreStartup,
1054                &crate::pre_startup::PreStartupMessage::SslRequest,
1055            ),
1056            Some(pre_startup::Event::SslRequest)
1057        );
1058        assert_eq!(
1059            pre_startup::project_external(
1060                pre_startup::RuntimeState::AwaitingSslReply,
1061                &crate::pre_startup::EncryptionReply::Accepted,
1062            ),
1063            Some(pre_startup::Event::Accept)
1064        );
1065    }
1066
1067    #[test]
1068    fn generated_server_pre_startup_is_the_client_facing_dual() {
1069        let _plaintext = server_pre_startup::Session::new()
1070            .ssl_request()
1071            .reject()
1072            .startup();
1073        let _encrypted = server_pre_startup::Session::new()
1074            .ssl_request()
1075            .accept()
1076            .complete()
1077            .startup();
1078
1079        let mut runtime = server_pre_startup::RuntimeFsm::new();
1080        assert_eq!(runtime.choice(), server_pre_startup::ChoiceKind::External);
1081        runtime.step(server_pre_startup::Event::SslRequest).unwrap();
1082        assert_eq!(runtime.choice(), server_pre_startup::ChoiceKind::Internal);
1083        assert!(runtime.step(server_pre_startup::Event::Startup).is_err());
1084        runtime.step(server_pre_startup::Event::Reject).unwrap();
1085        runtime.step(server_pre_startup::Event::Startup).unwrap();
1086        assert_eq!(runtime.state(), server_pre_startup::RuntimeState::Auth);
1087
1088        assert_eq!(
1089            pre_startup::RuntimeFsm::new().dual_event_choice(pre_startup::Event::SslRequest),
1090            Some(pre_startup::ChoiceKind::External)
1091        );
1092    }
1093
1094    #[test]
1095    fn generated_sasl_continuation_is_recursive() {
1096        let _typed = authentication::Session::new()
1097            .sasl()
1098            .initial()
1099            .continue_response()
1100            .response()
1101            .continue_response()
1102            .response()
1103            .final_response()
1104            .verified()
1105            .ok()
1106            .ready();
1107
1108        let mut runtime = authentication::RuntimeFsm::new();
1109        for event in [
1110            authentication::Event::Sasl,
1111            authentication::Event::Initial,
1112            authentication::Event::Continue,
1113            authentication::Event::Response,
1114            authentication::Event::Continue,
1115            authentication::Event::Response,
1116            authentication::Event::Final,
1117            authentication::Event::Verified,
1118            authentication::Event::Ok,
1119            authentication::Event::Ready,
1120        ] {
1121            runtime.step(event).unwrap();
1122        }
1123        assert_eq!(runtime.state(), authentication::RuntimeState::Ready);
1124        assert_eq!(
1125            authentication::project_external(
1126                authentication::RuntimeState::Sasl,
1127                &BackendMessage::Authentication(Authentication::SaslContinue(Bytes::from_static(
1128                    b"challenge"
1129                ),)),
1130            ),
1131            Some(authentication::Event::Continue)
1132        );
1133        assert_eq!(
1134            authentication::project_internal(
1135                authentication::RuntimeState::SaslChallenge,
1136                &FrontendMessage::PasswordResponse(Bytes::from_static(b"response")),
1137            ),
1138            Some(authentication::Event::Response)
1139        );
1140    }
1141
1142    #[test]
1143    fn generated_token_authentication_is_recursive() {
1144        let _typed = authentication::Session::new()
1145            .gss()
1146            .response()
1147            .continue_token()
1148            .response()
1149            .ok()
1150            .ready();
1151
1152        let mut runtime = authentication::RuntimeFsm::new();
1153        for event in [
1154            authentication::Event::Gss,
1155            authentication::Event::Response,
1156            authentication::Event::Continue,
1157            authentication::Event::Response,
1158            authentication::Event::Ok,
1159            authentication::Event::Ready,
1160        ] {
1161            runtime.step(event).unwrap();
1162        }
1163        assert_eq!(runtime.state(), authentication::RuntimeState::Ready);
1164    }
1165
1166    #[test]
1167    fn generated_server_authentication_keeps_mechanisms_independent() {
1168        let _typed = server_authentication::Session::new()
1169            .begin()
1170            .negotiate()
1171            .sasl()
1172            .initial()
1173            .continue_response()
1174            .response()
1175            .final_response()
1176            .ok()
1177            .parameter_status()
1178            .backend_key_data()
1179            .ready();
1180
1181        let mut runtime = server_authentication::RuntimeFsm::new();
1182        for event in [
1183            server_authentication::Event::Begin,
1184            server_authentication::Event::Negotiate,
1185            server_authentication::Event::Gss,
1186            server_authentication::Event::Response,
1187            server_authentication::Event::Continue,
1188            server_authentication::Event::Response,
1189            server_authentication::Event::Verified,
1190            server_authentication::Event::Ok,
1191            server_authentication::Event::Ready,
1192        ] {
1193            runtime.step(event).unwrap();
1194        }
1195        assert_eq!(runtime.state(), server_authentication::RuntimeState::Ready);
1196    }
1197
1198    #[test]
1199    fn runtime_fsm_tracks_the_handwritten_extended_typestate() {
1200        let message = StartupMessage {
1201            version: ProtocolVersion::V3_2,
1202            parameters: BTreeMap::new(),
1203        };
1204        let (startup, _) = Conn::new(()).startup(&message).unwrap();
1205        let AuthOffer::Ok(awaiting_ready) =
1206            startup.authentication().offer(Authentication::Ok).unwrap()
1207        else {
1208            panic!("authentication projected to the wrong branch")
1209        };
1210        let ready = awaiting_ready
1211            .offer_ready(SessionItem::ReadyForQuery {
1212                status: TransactionStatus::Idle,
1213                parameters_changed: false,
1214            })
1215            .unwrap();
1216        let mut runtime = RuntimeFsm::new();
1217
1218        let building = ready.begin_extended();
1219        runtime.step(Event::BeginExtended).unwrap();
1220        let (building, _) = building
1221            .push_parse(&Parse {
1222                statement: Bytes::from_static(b"s"),
1223                query: Bytes::from_static(b"select $1"),
1224                parameter_types: vec![23],
1225            })
1226            .unwrap();
1227        runtime.step(Event::Parse).unwrap();
1228        let (bound, _) = building
1229            .push_bind(&Bind {
1230                portal: Bytes::new(),
1231                statement: Bytes::from_static(b"s"),
1232                parameter_formats: vec![],
1233                parameters: vec![Some(Bytes::from_static(b"42"))],
1234                result_formats: vec![],
1235            })
1236            .unwrap();
1237        runtime.step(Event::Bind).unwrap();
1238        let (bound, _) = bound
1239            .push_execute(&Execute {
1240                portal: Bytes::new(),
1241                max_rows: 0,
1242            })
1243            .unwrap();
1244        runtime.step(Event::Execute).unwrap();
1245        let (awaiting_ready, _) = bound.push_sync();
1246        runtime.step(Event::Sync).unwrap();
1247        let AwaitingReadyTransition::Ready(ReadyState::Clean(ready)) =
1248            awaiting_ready.offer(SessionItem::ReadyForQuery {
1249                status: TransactionStatus::Idle,
1250                parameters_changed: false,
1251            })
1252        else {
1253            panic!("ready evidence projected to the wrong branch")
1254        };
1255        runtime.step(Event::Ready).unwrap();
1256
1257        assert_eq!(runtime.state(), RuntimeState::Ready);
1258        ready.into_transport();
1259    }
1260}