Skip to main content

pb_mapper_client/sdk/
types.rs

1//! Small value types shared across the SDK surface, and their conversions from
2//! the wire types in `pb-mapper-protocol`.
3//!
4//! Keeping owned mirrors here is what lets a caller depend on the SDK alone: the
5//! protocol crate stays an implementation detail.
6
7use pb_mapper_auth::LegacyProtocolPolicy;
8use pb_mapper_protocol::command::{PbConnStatusResp, PbServiceConnStatus};
9
10/// Transport used by a registered service or a local subscriber.
11#[derive(Clone, Copy, Debug, Eq, PartialEq)]
12pub enum Transport {
13    Tcp,
14    Udp,
15}
16
17impl Transport {
18    pub(crate) fn is_datagram(self) -> bool {
19        matches!(self, Self::Udp)
20    }
21}
22
23/// Observed lifecycle of a [`super::Registration`] or [`super::Connection`].
24#[derive(Clone, Debug, Eq, PartialEq)]
25pub enum TunnelStatus {
26    Starting,
27    Connected,
28    Retrying,
29    Stopped,
30    Failed(String),
31}
32
33impl TunnelStatus {
34    pub(crate) fn from_callback(status: &str) -> Self {
35        match status {
36            "connected" => return Self::Connected,
37            "retrying" => return Self::Retrying,
38            "failed" => return Self::Failed("failed".into()),
39            _ => {}
40        }
41        // A permanent rejection arrives as `failed: <reason>`; the reason is the
42        // only description of it the caller ever gets, so it has to survive here
43        // rather than collapsing into `Retrying` and hanging `wait_ready`.
44        match status.strip_prefix("failed:") {
45            Some(reason) => Self::Failed(reason.trim().to_string()),
46            None => Self::Retrying,
47        }
48    }
49}
50
51/// Relay mapping dump from `status remote-id`.
52#[derive(Clone, Debug, Eq, PartialEq)]
53pub struct RemoteId {
54    pub server_map: String,
55    pub active: String,
56    pub idle: String,
57}
58
59impl RemoteId {
60    pub(crate) fn from_status(status: PbConnStatusResp) -> super::Result<Self> {
61        match status {
62            PbConnStatusResp::RemoteId {
63                server_map,
64                active,
65                idle,
66            } => Ok(Self {
67                server_map,
68                active,
69                idle,
70            }),
71            other => Err(super::Error::protocol(format!(
72                "expected remote-id status, got {other:?}"
73            ))),
74        }
75    }
76}
77
78/// One control connection the relay is holding for a service key.
79#[derive(Clone, Debug, Eq, PartialEq)]
80pub struct ServiceConnection {
81    pub conn_id: u32,
82    pub generation: u64,
83    pub protocol_version: u16,
84    pub healthy: bool,
85    pub last_rx_age_ms: u64,
86}
87
88impl From<PbServiceConnStatus> for ServiceConnection {
89    fn from(value: PbServiceConnStatus) -> Self {
90        Self {
91            conn_id: value.conn_id,
92            generation: value.generation,
93            protocol_version: value.protocol_version,
94            healthy: value.healthy,
95            last_rx_age_ms: value.last_rx_age_ms,
96        }
97    }
98}
99
100/// Legacy framing policy, matching `pb-mapper admin legacy-protocol`.
101#[derive(Clone, Copy, Debug, Eq, PartialEq)]
102pub enum LegacyProtocol {
103    Allow,
104    Deny,
105}
106
107impl From<LegacyProtocol> for LegacyProtocolPolicy {
108    fn from(value: LegacyProtocol) -> Self {
109        match value {
110            LegacyProtocol::Allow => Self::Allow,
111            LegacyProtocol::Deny => Self::Deny,
112        }
113    }
114}
115
116impl From<LegacyProtocolPolicy> for LegacyProtocol {
117    fn from(value: LegacyProtocolPolicy) -> Self {
118        match value {
119            LegacyProtocolPolicy::Allow => Self::Allow,
120            LegacyProtocolPolicy::Deny => Self::Deny,
121        }
122    }
123}
124
125/// Per-connect path policy.
126///
127/// The default is [`Self::Auto`] when the SDK is built with the `p2p` feature,
128/// and [`Self::RelayOnly`] when it is not, since only `Auto` needs it.
129#[derive(Clone, Copy, Debug, Eq, PartialEq)]
130pub enum PathPolicy {
131    /// Use the relay for every connection, with the pre-direct protocol.
132    RelayOnly,
133    /// Try a direct QUIC path first; fall back to the relay if it cannot be
134    /// set up, before any application byte is sent.
135    Auto,
136}
137
138impl Default for PathPolicy {
139    fn default() -> Self {
140        if cfg!(feature = "p2p") {
141            Self::Auto
142        } else {
143            Self::RelayOnly
144        }
145    }
146}
147
148#[derive(Clone, Copy, Debug, Default)]
149pub struct ConnectOptions {
150    pub path: PathPolicy,
151}
152
153#[derive(Clone, Copy, Debug)]
154#[cfg_attr(not(feature = "p2p"), derive(Default))]
155pub struct RegisterOptions {
156    /// Accept direct sessions from authorized consumers. Defaults to `true`
157    /// with the `p2p` feature. Allowing it costs nothing until a consumer asks:
158    /// the UDP socket is opened on the first offer.
159    pub allow_direct: bool,
160    pub idle_timeout: Option<std::time::Duration>,
161}
162
163#[cfg(feature = "p2p")]
164impl Default for RegisterOptions {
165    fn default() -> Self {
166        Self {
167            allow_direct: true,
168            idle_timeout: None,
169        }
170    }
171}