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            ParameterDescription(parameter_description: Vec<u32>) => DescribeResponse <= crate::codec::BackendMessage::ParameterDescription(_),
302            RowDescription(row_description: crate::codec::RowDescription) => Building <= crate::codec::BackendMessage::RowDescription(_),
303            NoData(no_data) => Building <= crate::codec::BackendMessage::NoData,
304            Error(error: crate::codec::DiagnosticResponse) => ExtendedError <= crate::codec::BackendMessage::ErrorResponse(_),
305        }
306        ExecuteResponse internal {
307            Continue(continue_response: crate::codec::BackendMessage) => ExecuteResponse <= crate::codec::BackendMessage::RowDescription(_)
308                | crate::codec::BackendMessage::DataRow(_)
309                | crate::codec::BackendMessage::EmptyQueryResponse,
310            CopyIn(copy_in: crate::codec::CopyResponse) => ExtendedCopyIn <= crate::codec::BackendMessage::CopyInResponse(_),
311            CopyOut(copy_out: crate::codec::CopyResponse) => ExtendedCopyOut <= crate::codec::BackendMessage::CopyOutResponse(_),
312            CopyBoth(copy_both: crate::codec::CopyResponse) => ExtendedCopyBoth <= crate::codec::BackendMessage::CopyBothResponse(_),
313            CommandComplete(command_complete: bytes::Bytes) => Building <= crate::codec::BackendMessage::CommandComplete(_),
314            PortalSuspended(portal_suspended) => Building <= crate::codec::BackendMessage::PortalSuspended,
315            Error(error: crate::codec::DiagnosticResponse) => ExtendedError <= crate::codec::BackendMessage::ErrorResponse(_),
316        }
317        CloseResponse internal {
318            Complete(complete) => Building <= crate::codec::BackendMessage::CloseComplete,
319            Error(error: crate::codec::DiagnosticResponse) => ExtendedError <= crate::codec::BackendMessage::ErrorResponse(_),
320        }
321        ExtendedError external {
322            Discard(discard) => ExtendedError <= crate::codec::FrontendMessage::Parse(_)
323                | crate::codec::FrontendMessage::Bind(_)
324                | crate::codec::FrontendMessage::Describe(_)
325                | crate::codec::FrontendMessage::Execute(_)
326                | crate::codec::FrontendMessage::Close(_)
327                | crate::codec::FrontendMessage::Flush
328                | crate::codec::FrontendMessage::Query(_)
329                | crate::codec::FrontendMessage::FunctionCall(_)
330                | crate::codec::FrontendMessage::Terminate
331                | crate::codec::FrontendMessage::CopyData(_)
332                | crate::codec::FrontendMessage::CopyDone
333                | crate::codec::FrontendMessage::CopyFail(_)
334                | crate::codec::FrontendMessage::PasswordResponse(_),
335            Sync(sync) => SyncResponse <= crate::codec::FrontendMessage::Sync,
336        }
337        SyncResponse internal {
338            Ready(ready: crate::codec::TransactionStatus) => Ready <= crate::codec::BackendMessage::ReadyForQuery(_),
339        }
340        FunctionResponse internal {
341            Result(result: bytes::Bytes) => FunctionReady <= crate::codec::BackendMessage::FunctionCallResponse(_),
342            Error(error: crate::codec::DiagnosticResponse) => FunctionReady <= crate::codec::BackendMessage::ErrorResponse(_),
343        }
344        FunctionReady internal {
345            Ready(ready: crate::codec::TransactionStatus) => Ready <= crate::codec::BackendMessage::ReadyForQuery(_),
346        }
347        SimpleCopyIn external {
348            Data(data: bytes::Bytes) => SimpleCopyIn <= crate::codec::FrontendMessage::CopyData(_),
349            Done(done) => SimpleCopyInDone <= crate::codec::FrontendMessage::CopyDone,
350            Fail(fail: bytes::Bytes) => SimpleCopyInFailed <= crate::codec::FrontendMessage::CopyFail(_),
351        }
352        SimpleCopyInDone internal {
353            CommandComplete(command_complete: bytes::Bytes) => SimpleCopyReady <= crate::codec::BackendMessage::CommandComplete(_),
354        }
355        SimpleCopyInFailed internal {
356            Error(error: crate::codec::DiagnosticResponse) => SimpleCopyReady <= crate::codec::BackendMessage::ErrorResponse(_),
357        }
358        SimpleCopyOut internal {
359            Data(data: bytes::Bytes) => SimpleCopyOut <= crate::codec::BackendMessage::CopyData(_),
360            Done(done) => SimpleCopyOutDone <= crate::codec::BackendMessage::CopyDone,
361            Error(error: crate::codec::DiagnosticResponse) => SimpleCopyReady <= crate::codec::BackendMessage::ErrorResponse(_),
362        }
363        SimpleCopyOutDone internal {
364            CommandComplete(command_complete: bytes::Bytes) => SimpleCopyReady <= crate::codec::BackendMessage::CommandComplete(_),
365        }
366        SimpleCopyReady internal {
367            Ready(ready: crate::codec::TransactionStatus) => Ready <= crate::codec::BackendMessage::ReadyForQuery(_),
368        }
369        ExtendedCopyIn external {
370            Data(data: bytes::Bytes) => ExtendedCopyIn <= crate::codec::FrontendMessage::CopyData(_),
371            Done(done) => ExtendedCopyInDone <= crate::codec::FrontendMessage::CopyDone,
372            Fail(fail: bytes::Bytes) => ExtendedCopyInFailed <= crate::codec::FrontendMessage::CopyFail(_),
373        }
374        ExtendedCopyInDone internal {
375            CommandComplete(command_complete: bytes::Bytes) => Building <= crate::codec::BackendMessage::CommandComplete(_),
376        }
377        ExtendedCopyInFailed internal {
378            Error(error: crate::codec::DiagnosticResponse) => ExtendedError <= crate::codec::BackendMessage::ErrorResponse(_),
379        }
380        ExtendedCopyOut internal {
381            Data(data: bytes::Bytes) => ExtendedCopyOut <= crate::codec::BackendMessage::CopyData(_),
382            Done(done) => ExtendedCopyOutDone <= crate::codec::BackendMessage::CopyDone,
383            Error(error: crate::codec::DiagnosticResponse) => ExtendedError <= crate::codec::BackendMessage::ErrorResponse(_),
384        }
385        ExtendedCopyOutDone internal {
386            CommandComplete(command_complete: bytes::Bytes) => Building <= crate::codec::BackendMessage::CommandComplete(_),
387        }
388        SimpleCopyBoth mixed {
389            internal SendData(send_data: bytes::Bytes) => SimpleCopyBoth <= crate::codec::BackendMessage::CopyData(_),
390            external ReceiveData(receive_data: bytes::Bytes) => SimpleCopyBoth <= crate::codec::FrontendMessage::CopyData(_),
391            internal SendDone(send_done) => SimpleCopyBothServerDone <= crate::codec::BackendMessage::CopyDone,
392            external ReceiveDone(receive_done) => SimpleCopyBothClientDone <= crate::codec::FrontendMessage::CopyDone,
393            external Fail(fail: bytes::Bytes) => SimpleCopyBothFailed <= crate::codec::FrontendMessage::CopyFail(_),
394            internal Error(error: crate::codec::DiagnosticResponse) => SimpleCopyReady <= crate::codec::BackendMessage::ErrorResponse(_),
395        }
396        SimpleCopyBothClientDone internal {
397            SendData(send_data: bytes::Bytes) => SimpleCopyBothClientDone <= crate::codec::BackendMessage::CopyData(_),
398            SendDone(send_done) => SimpleCopyBothDone <= crate::codec::BackendMessage::CopyDone,
399            Error(error: crate::codec::DiagnosticResponse) => SimpleCopyReady <= crate::codec::BackendMessage::ErrorResponse(_),
400        }
401        SimpleCopyBothServerDone external {
402            ReceiveData(receive_data: bytes::Bytes) => SimpleCopyBothServerDone <= crate::codec::FrontendMessage::CopyData(_),
403            ReceiveDone(receive_done) => SimpleCopyBothDone <= crate::codec::FrontendMessage::CopyDone,
404            Fail(fail: bytes::Bytes) => SimpleCopyBothFailed <= crate::codec::FrontendMessage::CopyFail(_),
405        }
406        SimpleCopyBothDone internal {
407            CommandComplete(command_complete: bytes::Bytes) => SimpleCopyReady <= crate::codec::BackendMessage::CommandComplete(_),
408        }
409        SimpleCopyBothFailed internal {
410            Error(error: crate::codec::DiagnosticResponse) => SimpleCopyReady <= crate::codec::BackendMessage::ErrorResponse(_),
411        }
412        ExtendedCopyBoth mixed {
413            internal SendData(send_data: bytes::Bytes) => ExtendedCopyBoth <= crate::codec::BackendMessage::CopyData(_),
414            external ReceiveData(receive_data: bytes::Bytes) => ExtendedCopyBoth <= crate::codec::FrontendMessage::CopyData(_),
415            internal SendDone(send_done) => ExtendedCopyBothServerDone <= crate::codec::BackendMessage::CopyDone,
416            external ReceiveDone(receive_done) => ExtendedCopyBothClientDone <= crate::codec::FrontendMessage::CopyDone,
417            external Fail(fail: bytes::Bytes) => ExtendedCopyBothFailed <= crate::codec::FrontendMessage::CopyFail(_),
418            internal Error(error: crate::codec::DiagnosticResponse) => ExtendedError <= crate::codec::BackendMessage::ErrorResponse(_),
419        }
420        ExtendedCopyBothClientDone internal {
421            SendData(send_data: bytes::Bytes) => ExtendedCopyBothClientDone <= crate::codec::BackendMessage::CopyData(_),
422            SendDone(send_done) => ExtendedCopyBothDone <= crate::codec::BackendMessage::CopyDone,
423            Error(error: crate::codec::DiagnosticResponse) => ExtendedError <= crate::codec::BackendMessage::ErrorResponse(_),
424        }
425        ExtendedCopyBothServerDone external {
426            ReceiveData(receive_data: bytes::Bytes) => ExtendedCopyBothServerDone <= crate::codec::FrontendMessage::CopyData(_),
427            ReceiveDone(receive_done) => ExtendedCopyBothDone <= crate::codec::FrontendMessage::CopyDone,
428            Fail(fail: bytes::Bytes) => ExtendedCopyBothFailed <= crate::codec::FrontendMessage::CopyFail(_),
429        }
430        ExtendedCopyBothDone internal {
431            CommandComplete(command_complete: bytes::Bytes) => Building <= crate::codec::BackendMessage::CommandComplete(_),
432        }
433        ExtendedCopyBothFailed internal {
434            Error(error: crate::codec::DiagnosticResponse) => ExtendedError <= crate::codec::BackendMessage::ErrorResponse(_),
435        }
436        Terminated external {}
437    }
438}
439
440protocol! {
441    pub mod server_authentication {
442        initial Startup;
443        messages {
444            internal: crate::codec::BackendMessage,
445            external: crate::codec::FrontendMessage,
446        }
447        Startup internal {
448            Begin(begin) => Auth,
449            Reject(reject: crate::codec::DiagnosticResponse) => Terminated <= crate::codec::BackendMessage::ErrorResponse(_),
450        }
451        Auth internal {
452            Negotiate(negotiate: crate::codec::NegotiateProtocolVersion) => Auth <= crate::codec::BackendMessage::NegotiateProtocolVersion(_),
453            Cleartext(cleartext) => PasswordResponse <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::CleartextPassword),
454            Md5(md5: [u8; 4]) => PasswordResponse <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::Md5Password { .. }),
455            Sasl(sasl: Vec<bytes::Bytes>) => SaslInitial <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::Sasl { .. }),
456            Gss(gss) => TokenResponse <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::Gss),
457            Sspi(sspi) => TokenResponse <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::Sspi),
458            KerberosV5(kerberos_v5) => TokenResponse <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::KerberosV5),
459            Ok(ok) => StartupReady <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::Ok),
460            Error(error: crate::codec::DiagnosticResponse) => Terminated <= crate::codec::BackendMessage::ErrorResponse(_),
461        }
462        PasswordResponse external {
463            Response(response: bytes::Bytes) => Auth <= crate::codec::FrontendMessage::PasswordResponse(_),
464        }
465        SaslInitial external {
466            Initial(initial: crate::server_auth::SaslInitialResponse) => Sasl <= crate::codec::FrontendMessage::PasswordResponse(_),
467        }
468        Sasl internal {
469            Continue(continue_response: bytes::Bytes) => SaslResponse <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::SaslContinue(_)),
470            Final(final_response: bytes::Bytes) => Auth <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::SaslFinal(_)),
471            Error(error: crate::codec::DiagnosticResponse) => Terminated <= crate::codec::BackendMessage::ErrorResponse(_),
472        }
473        SaslResponse external {
474            Response(response: bytes::Bytes) => Sasl <= crate::codec::FrontendMessage::PasswordResponse(_),
475        }
476        TokenResponse external {
477            Response(response: bytes::Bytes) => TokenPolicy <= crate::codec::FrontendMessage::PasswordResponse(_),
478        }
479        TokenPolicy internal {
480            Continue(continue_token: bytes::Bytes) => TokenResponse <= crate::codec::BackendMessage::Authentication(crate::codec::Authentication::GssContinue(_)),
481            Verified(verified) => Auth,
482            Error(error: crate::codec::DiagnosticResponse) => Terminated <= crate::codec::BackendMessage::ErrorResponse(_),
483        }
484        StartupReady internal {
485            ParameterStatus(parameter_status: (bytes::Bytes, bytes::Bytes)) => StartupReady <= crate::codec::BackendMessage::ParameterStatus { .. },
486            BackendKeyData(backend_key_data: (u32, bytes::Bytes)) => StartupReady <= crate::codec::BackendMessage::BackendKeyData { .. },
487            Ready(ready: crate::codec::TransactionStatus) => Ready <= crate::codec::BackendMessage::ReadyForQuery(_),
488        }
489        Ready external {}
490        Terminated external {}
491    }
492}
493
494#[cfg(test)]
495mod tests {
496    use std::collections::BTreeMap;
497
498    use bytes::Bytes;
499
500    use super::{
501        authentication, backend, frontend, pre_startup, server_authentication, server_pre_startup,
502    };
503    use crate::{
504        Conn,
505        auth::AuthOffer,
506        codec::{
507            Authentication, BackendMessage, Bind, Execute, FrontendMessage, Parse,
508            TransactionStatus,
509        },
510        demux::SessionItem,
511        session::{AwaitingReadyTransition, ReadyState},
512        startup::{ProtocolVersion, StartupMessage},
513    };
514    use frontend::{Event, RuntimeFsm, RuntimeState, Session};
515
516    #[test]
517    fn railroad_labels_use_variant_syntax_and_link_payload_types() {
518        let svg = frontend::FRONTEND_RAILROAD_SVG;
519        assert!(svg.contains("◁ ReceiveCopyData(</tspan>"));
520        assert!(svg.contains("bytes::Bytes</tspan>"));
521        assert!(svg.contains(")</tspan>"));
522        assert!(svg.contains("xlink:href=\"https://docs.rs/bytes/1/bytes/struct.Bytes.html\""));
523        assert!(svg.contains("class=\"link\""));
524        assert!(svg.contains("▷ Query(</tspan>"));
525        assert!(svg.contains(") [Dirty]</tspan>"));
526        assert!(!svg.contains("class=\"link\"> <g class=\"terminal\""));
527        assert!(!svg.contains("&amp; ReceiveCopyData"));
528    }
529
530    macro_rules! exhaust_generated_runtime {
531        ($module:ident, $depth:expr) => {{
532            fn visit(runtime: $module::RuntimeFsm, depth: usize) {
533                for &event in $module::ALL_EVENTS {
534                    let state = runtime.state();
535                    let mut next = runtime;
536                    match next.step(event) {
537                        Ok(()) if depth > 0 => visit(next, depth - 1),
538                        Ok(()) => {}
539                        Err(error) => {
540                            assert_eq!(error.state, state);
541                            assert_eq!(error.event, event);
542                            assert_eq!(next.state(), state);
543                        }
544                    }
545                }
546            }
547            visit($module::RuntimeFsm::new(), $depth);
548        }};
549    }
550
551    #[test]
552    fn every_generated_protocol_exhausts_reachable_valid_and_invalid_events() {
553        exhaust_generated_runtime!(frontend, 5);
554        exhaust_generated_runtime!(backend, 5);
555        exhaust_generated_runtime!(pre_startup, 5);
556        exhaust_generated_runtime!(server_pre_startup, 5);
557        exhaust_generated_runtime!(authentication, 6);
558        exhaust_generated_runtime!(server_authentication, 6);
559    }
560
561    #[test]
562    fn generated_backend_projection_is_state_aware() {
563        let parse = FrontendMessage::Parse(Parse {
564            statement: Bytes::from_static(b"statement"),
565            query: Bytes::from_static(b"select 1"),
566            parameter_types: Vec::new(),
567        });
568        assert_eq!(
569            backend::project_external(backend::RuntimeState::Ready, &parse),
570            Some(backend::Event::Parse)
571        );
572        assert_eq!(
573            backend::project_external(backend::RuntimeState::SimpleCopyIn, &parse),
574            None
575        );
576        assert_eq!(
577            backend::project_external(backend::RuntimeState::ExtendedError, &FrontendMessage::Sync,),
578            Some(backend::Event::Sync)
579        );
580        assert_eq!(
581            backend::project_external(
582                backend::RuntimeState::ExtendedError,
583                &FrontendMessage::Flush,
584            ),
585            Some(backend::Event::Discard)
586        );
587        assert_eq!(
588            backend::project_internal(
589                backend::RuntimeState::Simple,
590                &BackendMessage::ReadyForQuery(TransactionStatus::Idle),
591            ),
592            Some(backend::Event::Ready)
593        );
594        assert_eq!(
595            backend::project_internal(
596                backend::RuntimeState::ExtendedCopyOut,
597                &BackendMessage::CopyDone,
598            ),
599            Some(backend::Event::Done)
600        );
601    }
602
603    #[test]
604    fn generated_frontend_projection_covers_wire_messages_only() {
605        assert_eq!(
606            frontend::project_internal(frontend::RuntimeState::Building, &FrontendMessage::Sync,),
607            Some(frontend::Event::Sync)
608        );
609        assert_eq!(
610            frontend::project_external(
611                frontend::RuntimeState::Simple,
612                &BackendMessage::ReadyForQuery(TransactionStatus::Idle),
613            ),
614            Some(frontend::Event::Ready)
615        );
616        assert_eq!(
617            frontend::project_external(
618                frontend::RuntimeState::ResetComplete,
619                &BackendMessage::ReadyForQuery(TransactionStatus::FailedTransaction),
620            ),
621            Some(frontend::Event::ReadyDirty)
622        );
623        assert_eq!(
624            frontend::project_external(
625                frontend::RuntimeState::CopyIn,
626                &BackendMessage::CopyData(Bytes::from_static(b"illegal direction")),
627            ),
628            None
629        );
630    }
631
632    #[test]
633    fn codec_messages_drive_generated_extended_and_copy_sequences() {
634        let parse = FrontendMessage::Parse(Parse {
635            statement: Bytes::new(),
636            query: Bytes::from_static(b"select $1"),
637            parameter_types: vec![23],
638        });
639        let mut extended = backend::RuntimeFsm::new();
640        extended
641            .step_projected(&parse, backend::project_external)
642            .unwrap();
643        extended
644            .step_projected(&BackendMessage::ParseComplete, backend::project_internal)
645            .unwrap();
646        extended
647            .step_projected(&FrontendMessage::Sync, backend::project_external)
648            .unwrap();
649        extended
650            .step_projected(
651                &BackendMessage::ReadyForQuery(TransactionStatus::Idle),
652                backend::project_internal,
653            )
654            .unwrap();
655        assert_eq!(extended.state(), backend::RuntimeState::Ready);
656
657        let mut copy = backend::RuntimeFsm::new();
658        copy.step_projected(
659            &FrontendMessage::Query(Bytes::from_static(b"copy t from stdin")),
660            backend::project_external,
661        )
662        .unwrap();
663        copy.step_projected(
664            &BackendMessage::CopyInResponse(crate::codec::CopyResponse {
665                overall_format: 0,
666                column_formats: vec![0],
667            }),
668            backend::project_internal,
669        )
670        .unwrap();
671        assert_eq!(copy.state(), backend::RuntimeState::SimpleCopyIn);
672        assert!(
673            copy.step_projected(
674                &FrontendMessage::Query(Bytes::from_static(b"select 1")),
675                backend::project_external,
676            )
677            .is_err()
678        );
679        assert_eq!(copy.state(), backend::RuntimeState::SimpleCopyIn);
680    }
681
682    #[test]
683    fn generated_typestate_and_runtime_accept_the_extended_loop() {
684        let _typed = Session::new()
685            .begin_extended()
686            .parse()
687            .bind()
688            .execute()
689            .sync()
690            .ready();
691
692        let mut runtime = RuntimeFsm::new();
693        for event in [
694            Event::BeginExtended,
695            Event::Parse,
696            Event::Bind,
697            Event::Execute,
698            Event::Sync,
699            Event::Ready,
700        ] {
701            runtime.step(event).unwrap();
702        }
703        assert_eq!(runtime.state(), RuntimeState::Ready);
704    }
705
706    #[test]
707    fn generated_backend_discards_failed_pipeline_until_sync() {
708        let _typed = backend::Session::new()
709            .parse()
710            .error()
711            .discard()
712            .discard()
713            .sync()
714            .ready()
715            .terminate();
716
717        let mut runtime = backend::RuntimeFsm::new();
718        for event in [
719            backend::Event::Parse,
720            backend::Event::Error,
721            backend::Event::Discard,
722            backend::Event::Discard,
723            backend::Event::Sync,
724            backend::Event::Ready,
725            backend::Event::Terminate,
726        ] {
727            runtime.step(event).unwrap();
728        }
729        assert_eq!(runtime.state(), backend::RuntimeState::Terminated);
730    }
731
732    #[test]
733    fn generated_backend_copy_resumes_its_enclosing_session() {
734        let _simple = backend::Session::new()
735            .query()
736            .copy_in()
737            .data()
738            .done()
739            .command_complete()
740            .ready();
741        let _extended = backend::Session::new()
742            .execute()
743            .copy_out()
744            .data()
745            .done()
746            .command_complete()
747            .sync()
748            .ready();
749
750        let mut runtime = backend::RuntimeFsm::new();
751        for event in [
752            backend::Event::Execute,
753            backend::Event::CopyOut,
754            backend::Event::Data,
755            backend::Event::Done,
756            backend::Event::CommandComplete,
757        ] {
758            runtime.step(event).unwrap();
759        }
760        assert_eq!(runtime.state(), backend::RuntimeState::Building);
761        runtime.step(backend::Event::Sync).unwrap();
762        runtime.step(backend::Event::Ready).unwrap();
763        assert_eq!(runtime.state(), backend::RuntimeState::Ready);
764    }
765
766    #[test]
767    fn generated_backend_copy_both_tracks_independent_half_closes() {
768        let _server_first = backend::Session::new()
769            .query()
770            .copy_both()
771            .send_data()
772            .receive_data()
773            .send_done()
774            .receive_data()
775            .receive_done()
776            .command_complete()
777            .ready();
778        let _client_first = backend::Session::new()
779            .execute()
780            .copy_both()
781            .receive_done()
782            .send_data()
783            .send_done()
784            .command_complete()
785            .sync()
786            .ready();
787
788        let mut runtime = backend::RuntimeFsm::new();
789        runtime.step(backend::Event::Query).unwrap();
790        runtime.step(backend::Event::CopyBoth).unwrap();
791        assert_eq!(runtime.choice(), backend::ChoiceKind::Mixed);
792        assert_eq!(
793            runtime.event_choice(backend::Event::SendData),
794            Some(backend::ChoiceKind::Internal)
795        );
796        assert_eq!(
797            runtime.event_choice(backend::Event::ReceiveData),
798            Some(backend::ChoiceKind::External)
799        );
800        runtime.step(backend::Event::SendDone).unwrap();
801        assert!(runtime.step(backend::Event::SendData).is_err());
802        runtime.step(backend::Event::ReceiveDone).unwrap();
803        runtime.step(backend::Event::CommandComplete).unwrap();
804        runtime.step(backend::Event::Ready).unwrap();
805        assert_eq!(runtime.state(), backend::RuntimeState::Ready);
806    }
807
808    #[test]
809    fn generated_runtime_rejects_query_during_copy() {
810        let mut runtime = RuntimeFsm::new();
811        runtime.step(Event::Query).unwrap();
812        runtime.step(Event::CopyIn).unwrap();
813        assert!(runtime.step(Event::Query).is_err());
814        assert_eq!(
815            runtime.event_choice(Event::Error),
816            Some(frontend::ChoiceKind::External)
817        );
818        runtime.step(Event::Error).unwrap();
819        assert_eq!(runtime.state(), RuntimeState::Draining);
820    }
821
822    #[test]
823    fn generated_copy_both_waits_for_both_half_closes() {
824        let mut directions = RuntimeFsm::new();
825        directions.step(Event::Query).unwrap();
826        directions.step(Event::CopyBoth).unwrap();
827        assert_eq!(directions.choice(), frontend::ChoiceKind::Mixed);
828        assert_eq!(
829            directions.event_choice(Event::SendCopyData),
830            Some(frontend::ChoiceKind::Internal)
831        );
832        assert_eq!(
833            directions.event_choice(Event::ReceiveCopyData),
834            Some(frontend::ChoiceKind::External)
835        );
836
837        let mut client_first = RuntimeFsm::new();
838        for event in [
839            Event::Query,
840            Event::CopyBoth,
841            Event::SendCopyDone,
842            Event::ReceiveCopyData,
843            Event::ReceiveCopyDone,
844        ] {
845            client_first.step(event).unwrap();
846        }
847        assert_eq!(client_first.state(), RuntimeState::AwaitingReady);
848
849        let mut server_first = RuntimeFsm::new();
850        for event in [
851            Event::Query,
852            Event::CopyBoth,
853            Event::ReceiveCopyDone,
854            Event::SendCopyData,
855            Event::SendCopyDone,
856        ] {
857            server_first.step(event).unwrap();
858        }
859        assert_eq!(server_first.state(), RuntimeState::AwaitingReady);
860    }
861
862    #[test]
863    fn generated_function_call_and_termination_match_typed_paths() {
864        let _function = Session::new()
865            .function_call()
866            .function_response()
867            .ready()
868            .terminate();
869
870        let mut runtime = RuntimeFsm::new();
871        for event in [
872            Event::FunctionCall,
873            Event::FunctionResponse,
874            Event::Ready,
875            Event::Terminate,
876        ] {
877            runtime.step(event).unwrap();
878        }
879        assert_eq!(runtime.state(), RuntimeState::Terminated);
880    }
881
882    #[test]
883    fn generated_pool_reset_requires_discard_and_ready_evidence() {
884        let _typed = Session::new()
885            .reset()
886            .continue_reset()
887            .discard_complete()
888            .continue_reset()
889            .ready_clean();
890
891        let mut runtime = RuntimeFsm::new();
892        runtime.step(Event::Reset).unwrap();
893        assert!(runtime.step(Event::ReadyClean).is_err());
894        runtime.step(Event::DiscardComplete).unwrap();
895        runtime.step(Event::ReadyClean).unwrap();
896        assert_eq!(runtime.state(), RuntimeState::Ready);
897
898        let dirty: Conn<(), crate::auth::Ready, crate::Dirty> =
899            Conn::new(()).transition().mark_dirty();
900        let (resetting, _) = dirty.begin_reset().unwrap();
901        let crate::session::ResettingTransition::Complete(complete) =
902            resetting.offer(SessionItem::CommandComplete {
903                tag: Bytes::from_static(b"DISCARD ALL"),
904                command: crate::demux::CommandIndex(1),
905                notices: Vec::new(),
906            })
907        else {
908            panic!("DISCARD ALL did not advance reset recovery")
909        };
910        let crate::session::ResetCompleteTransition::Ready(ready) =
911            complete.offer(SessionItem::ReadyForQuery {
912                status: TransactionStatus::Idle,
913                parameters_changed: false,
914            })
915        else {
916            panic!("idle readiness did not restore pristine evidence")
917        };
918        ready.release();
919    }
920
921    #[test]
922    fn generated_transport_session_tracks_cleanliness_effects() {
923        #[derive(Debug)]
924        struct InitiallyClean;
925
926        let ready: frontend::TypedSession<(), frontend::Ready, InitiallyClean> =
927            frontend::TypedSession::with_transport(());
928        let (dirty, query): (
929            frontend::TypedSession<(), frontend::Simple, frontend::Dirty>,
930            Bytes,
931        ) = ready
932            .query(Bytes::from_static(b"select 1"), |(), query| {
933                Ok::<_, std::convert::Infallible>(query)
934            })
935            .expect("query handler is infallible");
936        assert_eq!(query, Bytes::from_static(b"select 1"));
937        let (dirty, _status): (
938            frontend::TypedSession<(), frontend::Ready, frontend::Dirty>,
939            TransactionStatus,
940        ) = dirty
941            .ready(TransactionStatus::Idle, |(), status| {
942                Ok::<_, std::convert::Infallible>(status)
943            })
944            .expect("readiness handler is infallible");
945        assert_eq!(dirty.into_transport(), ());
946
947        let dirty: frontend::TypedSession<(), frontend::Ready, frontend::Dirty> =
948            frontend::TypedSession::with_transport(());
949        let (reset_complete, _tag) = dirty
950            .reset()
951            .discard_complete(Bytes::from_static(b"DISCARD ALL"), |(), tag| {
952                Ok::<_, std::convert::Infallible>(tag)
953            })
954            .expect("command handler is infallible");
955        let (clean, _status): (
956            frontend::TypedSession<(), frontend::Ready, frontend::Pristine>,
957            TransactionStatus,
958        ) = reset_complete
959            .ready_clean(TransactionStatus::Idle, |(), status| {
960                Ok::<_, std::convert::Infallible>(status)
961            })
962            .expect("readiness handler is infallible");
963        assert_eq!(clean.into_transport(), ());
964    }
965
966    #[test]
967    fn generated_frontend_parse_payload_is_inspectable_and_fallible() {
968        #[derive(Debug)]
969        struct Clean;
970
971        let ready: frontend::TypedSession<Vec<crate::codec::Frame>, frontend::Ready, Clean> =
972            frontend::TypedSession::with_transport(Vec::new());
973        let parse = Parse {
974            statement: Bytes::from_static(b"statement"),
975            query: Bytes::from_static(b"select encrypted_column"),
976            parameter_types: vec![23],
977        };
978        let (building, query): (
979            frontend::TypedSession<Vec<crate::codec::Frame>, frontend::Building, frontend::Dirty>,
980            Bytes,
981        ) = ready
982            .begin_extended()
983            .parse(parse, |frames, message| {
984                let query = message.query.clone();
985                frames.push(message.to_frame()?);
986                Ok::<_, std::io::Error>(query)
987            })
988            .unwrap();
989        assert_eq!(query, Bytes::from_static(b"select encrypted_column"));
990        assert_eq!(building.into_transport()[0].tag, b'P');
991
992        let ready: frontend::TypedSession<Vec<crate::codec::Frame>, frontend::Ready, Clean> =
993            frontend::TypedSession::with_transport(Vec::new());
994        let invalid = Parse {
995            statement: Bytes::from_static(b"bad\0statement"),
996            query: Bytes::from_static(b"select 1"),
997            parameter_types: vec![],
998        };
999        let (building, error) = ready
1000            .begin_extended()
1001            .parse(invalid, |frames, message| {
1002                frames.push(message.to_frame()?);
1003                Ok::<_, std::io::Error>(())
1004            })
1005            .unwrap_err();
1006        assert_eq!(error.kind(), std::io::ErrorKind::InvalidInput);
1007        assert!(building.into_transport().is_empty());
1008    }
1009
1010    #[test]
1011    fn generated_pre_startup_requires_handshake_before_startup() {
1012        struct Clean;
1013        struct Tcp;
1014        struct Tls(Tcp);
1015
1016        let _typed = pre_startup::Session::new()
1017            .ssl_request()
1018            .accept()
1019            .complete()
1020            .startup();
1021
1022        let mut runtime = pre_startup::RuntimeFsm::new();
1023        runtime.step(pre_startup::Event::SslRequest).unwrap();
1024        assert!(runtime.step(pre_startup::Event::Startup).is_err());
1025        runtime.step(pre_startup::Event::Accept).unwrap();
1026        runtime.step(pre_startup::Event::HandshakeComplete).unwrap();
1027        runtime.step(pre_startup::Event::Startup).unwrap();
1028
1029        let pre_startup: pre_startup::TypedSession<Tcp, pre_startup::PreStartup, Clean> =
1030            pre_startup::TypedSession::with_transport(Tcp);
1031        let startup = crate::startup::StartupMessage {
1032            version: crate::startup::ProtocolVersion::V3_0,
1033            parameters: BTreeMap::new(),
1034        };
1035        let auth = pre_startup
1036            .ssl_request()
1037            .accept()
1038            .map_transport(Tls)
1039            .complete()
1040            .startup(startup, |_, startup| {
1041                Ok::<_, std::convert::Infallible>(startup)
1042            });
1043        let (auth, _startup): (
1044            pre_startup::TypedSession<Tls, pre_startup::Auth, Clean>,
1045            crate::startup::StartupMessage,
1046        ) = match auth {
1047            Ok(success) => success,
1048            Err((_session, never)) => match never {},
1049        };
1050        let Tls(_tcp) = auth.into_transport();
1051
1052        assert_eq!(
1053            pre_startup::project_internal(
1054                pre_startup::RuntimeState::PreStartup,
1055                &crate::pre_startup::PreStartupMessage::SslRequest,
1056            ),
1057            Some(pre_startup::Event::SslRequest)
1058        );
1059        assert_eq!(
1060            pre_startup::project_external(
1061                pre_startup::RuntimeState::AwaitingSslReply,
1062                &crate::pre_startup::EncryptionReply::Accepted,
1063            ),
1064            Some(pre_startup::Event::Accept)
1065        );
1066    }
1067
1068    #[test]
1069    fn generated_server_pre_startup_is_the_client_facing_dual() {
1070        let _plaintext = server_pre_startup::Session::new()
1071            .ssl_request()
1072            .reject()
1073            .startup();
1074        let _encrypted = server_pre_startup::Session::new()
1075            .ssl_request()
1076            .accept()
1077            .complete()
1078            .startup();
1079
1080        let mut runtime = server_pre_startup::RuntimeFsm::new();
1081        assert_eq!(runtime.choice(), server_pre_startup::ChoiceKind::External);
1082        runtime.step(server_pre_startup::Event::SslRequest).unwrap();
1083        assert_eq!(runtime.choice(), server_pre_startup::ChoiceKind::Internal);
1084        assert!(runtime.step(server_pre_startup::Event::Startup).is_err());
1085        runtime.step(server_pre_startup::Event::Reject).unwrap();
1086        runtime.step(server_pre_startup::Event::Startup).unwrap();
1087        assert_eq!(runtime.state(), server_pre_startup::RuntimeState::Auth);
1088
1089        assert_eq!(
1090            pre_startup::RuntimeFsm::new().dual_event_choice(pre_startup::Event::SslRequest),
1091            Some(pre_startup::ChoiceKind::External)
1092        );
1093    }
1094
1095    #[test]
1096    fn generated_sasl_continuation_is_recursive() {
1097        let _typed = authentication::Session::new()
1098            .sasl()
1099            .initial()
1100            .continue_response()
1101            .response()
1102            .continue_response()
1103            .response()
1104            .final_response()
1105            .verified()
1106            .ok()
1107            .ready();
1108
1109        let mut runtime = authentication::RuntimeFsm::new();
1110        for event in [
1111            authentication::Event::Sasl,
1112            authentication::Event::Initial,
1113            authentication::Event::Continue,
1114            authentication::Event::Response,
1115            authentication::Event::Continue,
1116            authentication::Event::Response,
1117            authentication::Event::Final,
1118            authentication::Event::Verified,
1119            authentication::Event::Ok,
1120            authentication::Event::Ready,
1121        ] {
1122            runtime.step(event).unwrap();
1123        }
1124        assert_eq!(runtime.state(), authentication::RuntimeState::Ready);
1125        assert_eq!(
1126            authentication::project_external(
1127                authentication::RuntimeState::Sasl,
1128                &BackendMessage::Authentication(Authentication::SaslContinue(Bytes::from_static(
1129                    b"challenge"
1130                ),)),
1131            ),
1132            Some(authentication::Event::Continue)
1133        );
1134        assert_eq!(
1135            authentication::project_internal(
1136                authentication::RuntimeState::SaslChallenge,
1137                &FrontendMessage::PasswordResponse(Bytes::from_static(b"response")),
1138            ),
1139            Some(authentication::Event::Response)
1140        );
1141    }
1142
1143    #[test]
1144    fn generated_token_authentication_is_recursive() {
1145        let _typed = authentication::Session::new()
1146            .gss()
1147            .response()
1148            .continue_token()
1149            .response()
1150            .ok()
1151            .ready();
1152
1153        let mut runtime = authentication::RuntimeFsm::new();
1154        for event in [
1155            authentication::Event::Gss,
1156            authentication::Event::Response,
1157            authentication::Event::Continue,
1158            authentication::Event::Response,
1159            authentication::Event::Ok,
1160            authentication::Event::Ready,
1161        ] {
1162            runtime.step(event).unwrap();
1163        }
1164        assert_eq!(runtime.state(), authentication::RuntimeState::Ready);
1165    }
1166
1167    #[test]
1168    fn generated_server_authentication_keeps_mechanisms_independent() {
1169        let _typed = server_authentication::Session::new()
1170            .begin()
1171            .negotiate()
1172            .sasl()
1173            .initial()
1174            .continue_response()
1175            .response()
1176            .final_response()
1177            .ok()
1178            .parameter_status()
1179            .backend_key_data()
1180            .ready();
1181
1182        let mut runtime = server_authentication::RuntimeFsm::new();
1183        for event in [
1184            server_authentication::Event::Begin,
1185            server_authentication::Event::Negotiate,
1186            server_authentication::Event::Gss,
1187            server_authentication::Event::Response,
1188            server_authentication::Event::Continue,
1189            server_authentication::Event::Response,
1190            server_authentication::Event::Verified,
1191            server_authentication::Event::Ok,
1192            server_authentication::Event::Ready,
1193        ] {
1194            runtime.step(event).unwrap();
1195        }
1196        assert_eq!(runtime.state(), server_authentication::RuntimeState::Ready);
1197    }
1198
1199    #[test]
1200    fn runtime_fsm_tracks_the_handwritten_extended_typestate() {
1201        let message = StartupMessage {
1202            version: ProtocolVersion::V3_2,
1203            parameters: BTreeMap::new(),
1204        };
1205        let (startup, _) = Conn::new(()).startup(&message).unwrap();
1206        let AuthOffer::Ok(awaiting_ready) =
1207            startup.authentication().offer(Authentication::Ok).unwrap()
1208        else {
1209            panic!("authentication projected to the wrong branch")
1210        };
1211        let ready = awaiting_ready
1212            .offer_ready(SessionItem::ReadyForQuery {
1213                status: TransactionStatus::Idle,
1214                parameters_changed: false,
1215            })
1216            .unwrap();
1217        let mut runtime = RuntimeFsm::new();
1218
1219        let building = ready.begin_extended();
1220        runtime.step(Event::BeginExtended).unwrap();
1221        let (building, _) = building
1222            .push_parse(&Parse {
1223                statement: Bytes::from_static(b"s"),
1224                query: Bytes::from_static(b"select $1"),
1225                parameter_types: vec![23],
1226            })
1227            .unwrap();
1228        runtime.step(Event::Parse).unwrap();
1229        let (bound, _) = building
1230            .push_bind(&Bind {
1231                portal: Bytes::new(),
1232                statement: Bytes::from_static(b"s"),
1233                parameter_formats: vec![],
1234                parameters: vec![Some(Bytes::from_static(b"42"))],
1235                result_formats: vec![],
1236            })
1237            .unwrap();
1238        runtime.step(Event::Bind).unwrap();
1239        let (bound, _) = bound
1240            .push_execute(&Execute {
1241                portal: Bytes::new(),
1242                max_rows: 0,
1243            })
1244            .unwrap();
1245        runtime.step(Event::Execute).unwrap();
1246        let (awaiting_ready, _) = bound.push_sync();
1247        runtime.step(Event::Sync).unwrap();
1248        let AwaitingReadyTransition::Ready(ReadyState::Clean(ready)) =
1249            awaiting_ready.offer(SessionItem::ReadyForQuery {
1250                status: TransactionStatus::Idle,
1251                parameters_changed: false,
1252            })
1253        else {
1254            panic!("ready evidence projected to the wrong branch")
1255        };
1256        runtime.step(Event::Ready).unwrap();
1257
1258        assert_eq!(runtime.state(), RuntimeState::Ready);
1259        ready.into_transport();
1260    }
1261}