Skip to main content

pg_proto/
lib.rs

1//! Session-typed `PostgreSQL` wire protocol primitives.
2
3pub mod auth;
4pub mod cancel;
5pub mod cleanliness;
6pub mod codec;
7pub mod credentials;
8pub mod demux;
9pub mod erased;
10pub mod grammar;
11pub mod integrations;
12pub mod intermediary;
13pub mod pipeline;
14pub mod pre_startup;
15pub mod replication;
16pub mod resources;
17pub mod scram;
18pub mod server_auth;
19pub mod server_session;
20pub mod session;
21pub mod startup;
22pub mod tls;
23pub mod transport;
24
25use std::marker::PhantomData;
26
27/// A connection whose legal operations are selected by `Phase` and `Cleanliness`.
28#[must_use = "dropping a connection abandons the PostgreSQL session"]
29#[derive(Debug)]
30pub struct Conn<Transport, Phase, Cleanliness = Pristine> {
31    transport: Option<Transport>,
32    _state: PhantomData<(Phase, Cleanliness)>,
33}
34
35impl<Transport, Phase, Cleanliness> Conn<Transport, Phase, Cleanliness> {
36    pub(crate) fn transition<NextPhase, NextCleanliness>(
37        mut self,
38    ) -> Conn<Transport, NextPhase, NextCleanliness> {
39        Conn {
40            transport: self.transport.take(),
41            _state: PhantomData,
42        }
43    }
44
45    /// Returns the underlying transport when deliberately leaving the typed API.
46    ///
47    /// # Panics
48    ///
49    /// Panics only if an internal transition has already moved the transport.
50    pub fn into_transport(mut self) -> Transport {
51        self.transport
52            .take()
53            .expect("live connection has a transport")
54    }
55
56    /// Changes transport representation without changing either state index.
57    ///
58    /// # Panics
59    ///
60    /// Panics only if an internal transition has already moved the transport.
61    pub fn map_transport<Next>(
62        mut self,
63        map: impl FnOnce(Transport) -> Next,
64    ) -> Conn<Next, Phase, Cleanliness> {
65        Conn {
66            transport: Some(map(self
67                .transport
68                .take()
69                .expect("live connection has a transport"))),
70            _state: PhantomData,
71        }
72    }
73
74    pub(crate) const fn transport(&self) -> &Transport {
75        match &self.transport {
76            Some(transport) => transport,
77            None => panic!("connection transport has already moved"),
78        }
79    }
80
81    pub(crate) const fn transport_mut(&mut self) -> &mut Transport {
82        match &mut self.transport {
83            Some(transport) => transport,
84            None => panic!("connection transport has already moved"),
85        }
86    }
87}
88
89impl<Transport> Conn<Transport, pre_startup::PreStartup, Pristine> {
90    /// Starts a new connection before any startup packet has been sent.
91    pub const fn new(transport: Transport) -> Self {
92        Self {
93            transport: Some(transport),
94            _state: PhantomData,
95        }
96    }
97}
98
99#[cfg(debug_assertions)]
100impl<Transport, Phase, Cleanliness> Drop for Conn<Transport, Phase, Cleanliness> {
101    fn drop(&mut self) {
102        assert!(
103            self.transport.is_none() || std::thread::panicking(),
104            "live PostgreSQL connection dropped before a terminal transition; call into_transport() to abort deliberately"
105        );
106    }
107}
108
109/// The connection has no known session-local changes.
110#[derive(Debug)]
111pub enum Pristine {}
112
113/// The connection has state which prevents unconditional pool release.
114#[derive(Debug)]
115pub enum Dirty {}