fraiseql_wire/error/
mod.rs1use std::io;
4use thiserror::Error;
5
6#[derive(Debug, Error)]
8#[non_exhaustive]
9pub enum WireError {
10 #[error("connection error: {0}")]
12 Connection(String),
13
14 #[error("authentication failed: {0}")]
16 Authentication(String),
17
18 #[error("protocol error: {0}")]
20 Protocol(String),
21
22 #[error("sql error: {0}")]
24 Sql(String),
25
26 #[error("json decode error: {0}")]
28 JsonDecode(#[from] serde_json::Error),
29
30 #[error("io error: {0}")]
32 Io(#[from] io::Error),
33
34 #[error("invalid configuration: {0}")]
36 Config(String),
37
38 #[error("query cancelled")]
40 Cancelled,
41
42 #[error("invalid result schema: {0}")]
44 InvalidSchema(String),
45
46 #[error("connection busy: {0}")]
48 ConnectionBusy(String),
49
50 #[error("invalid connection state: expected {expected}, got {actual}")]
52 InvalidState {
53 expected: String,
55 actual: String,
57 },
58
59 #[error("connection closed")]
61 ConnectionClosed,
62
63 #[error("deserialization error for type '{type_name}': {details}")]
68 Deserialization {
69 type_name: String,
71 details: String,
73 },
74
75 #[error("memory limit exceeded: {estimated_memory} bytes buffered > {limit} bytes limit")]
90 MemoryLimitExceeded {
91 limit: usize,
93 estimated_memory: usize,
95 },
96}
97
98pub type Result<T> = std::result::Result<T, WireError>;
100
101impl WireError {
102 pub fn connection<S: Into<String>>(msg: S) -> Self {
104 WireError::Connection(msg.into())
105 }
106
107 #[must_use]
109 pub fn connection_refused(host: &str, port: u16) -> Self {
110 WireError::Connection(format!(
111 "failed to connect to {}:{}: connection refused. \
112 Is Postgres running? Verify with: pg_isready -h {} -p {}",
113 host, port, host, port
114 ))
115 }
116
117 pub fn protocol<S: Into<String>>(msg: S) -> Self {
119 WireError::Protocol(msg.into())
120 }
121
122 pub fn sql<S: Into<String>>(msg: S) -> Self {
124 WireError::Sql(msg.into())
125 }
126
127 #[must_use]
129 pub fn invalid_schema_columns(num_columns: usize) -> Self {
130 WireError::InvalidSchema(format!(
131 "query returned {} columns instead of 1. \
132 fraiseql-wire supports only: SELECT data FROM <view>. \
133 See troubleshooting.md#error-invalid-result-schema",
134 num_columns
135 ))
136 }
137
138 pub fn invalid_schema<S: Into<String>>(msg: S) -> Self {
140 WireError::InvalidSchema(msg.into())
141 }
142
143 #[must_use]
145 pub fn auth_failed(username: &str, reason: &str) -> Self {
146 WireError::Authentication(format!(
147 "authentication failed for user '{}': {}. \
148 Verify credentials with: psql -U {} -W",
149 username, reason, username
150 ))
151 }
152
153 pub fn config_invalid<S: Into<String>>(msg: S) -> Self {
155 WireError::Config(format!(
156 "invalid configuration: {}. \
157 Expected format: postgres://[user[:password]@][host[:port]]/[database]",
158 msg.into()
159 ))
160 }
161
162 #[must_use]
173 pub const fn is_retriable(&self) -> bool {
174 matches!(self, WireError::Io(_) | WireError::ConnectionClosed)
175 }
176
177 #[must_use]
181 pub const fn category(&self) -> &'static str {
182 match self {
183 WireError::Connection(_) => "connection",
184 WireError::Authentication(_) => "authentication",
185 WireError::Protocol(_) => "protocol",
186 WireError::Sql(_) => "sql",
187 WireError::JsonDecode(_) => "json_decode",
188 WireError::Io(_) => "io",
189 WireError::Config(_) => "config",
190 WireError::Cancelled => "cancelled",
191 WireError::InvalidSchema(_) => "invalid_schema",
192 WireError::ConnectionBusy(_) => "connection_busy",
193 WireError::InvalidState { .. } => "invalid_state",
194 WireError::ConnectionClosed => "connection_closed",
195 WireError::Deserialization { .. } => "deserialization",
196 WireError::MemoryLimitExceeded { .. } => "memory_limit_exceeded",
197 }
198 }
199}
200
201#[cfg(test)]
202mod tests;