1use crate::prelude::{Box, String, ToString, Vec, format};
4use compact_str::CompactString;
5use thiserror::Error;
6
7const MAX_CONTEXT_PARAMS: usize = 32;
8const MAX_CONTEXT_PARAM_CHARS: usize = 128;
9
10#[derive(Debug, Clone)]
12pub struct QueryContext {
13 pub sql: CompactString,
15 pub params: Box<[CompactString]>,
17 pub param_count: usize,
19}
20
21impl QueryContext {
22 pub fn new<V: core::fmt::Debug>(sql: &str, params: &[&V]) -> Self {
24 let rendered = params
25 .iter()
26 .take(MAX_CONTEXT_PARAMS)
27 .map(|param| truncate_param(format!("{param:?}")))
28 .collect::<Vec<_>>()
29 .into_boxed_slice();
30
31 Self {
32 sql: sql.into(),
33 params: rendered,
34 param_count: params.len(),
35 }
36 }
37
38 fn params_display(&self) -> String {
39 if self.param_count == 0 {
40 return "[]".to_string();
41 }
42
43 let mut rendered = String::from("[");
44 for (index, param) in self.params.iter().enumerate() {
45 if index > 0 {
46 rendered.push_str(", ");
47 }
48 rendered.push_str(param.as_str());
49 }
50 if self.param_count > self.params.len() {
51 if !self.params.is_empty() {
52 rendered.push_str(", ");
53 }
54 rendered.push_str("...");
55 rendered.push_str(&format!("(+{} more)", self.param_count - self.params.len()));
56 }
57 rendered.push(']');
58 rendered
59 }
60}
61
62fn truncate_param(mut value: String) -> CompactString {
63 if value.chars().count() <= MAX_CONTEXT_PARAM_CHARS {
64 return value.into();
65 }
66
67 let mut truncated = String::new();
68 for ch in value.drain(..).take(MAX_CONTEXT_PARAM_CHARS) {
69 truncated.push(ch);
70 }
71 truncated.push_str("...");
72 truncated.into()
73}
74
75#[derive(Debug, Error)]
77pub enum DrizzleError {
78 #[error("Execution error: {0}")]
80 ExecutionError(compact_str::CompactString),
81
82 #[error("Prepare error: {0}")]
84 PrepareError(compact_str::CompactString),
85
86 #[error("No rows found")]
88 NotFound,
89
90 #[error("Transaction error: {0}")]
92 TransactionError(compact_str::CompactString),
93
94 #[error("Mapping error: {0}")]
96 Mapping(compact_str::CompactString),
97
98 #[error("Statement error: {0}")]
100 Statement(compact_str::CompactString),
101
102 #[error("Query error: {0}")]
104 Query(CompactString),
105
106 #[error("{source}\n sql: {sql}\n params: {params}", sql = .ctx.sql, params = .ctx.params_display())]
108 QueryFailed {
109 ctx: Box<QueryContext>,
111 #[source]
113 source: Box<DrizzleError>,
114 },
115
116 #[error("Parameter conversion error: {0}")]
118 ParameterError(compact_str::CompactString),
119
120 #[error("Integer conversion error: {0}")]
122 TryFromInt(#[from] core::num::TryFromIntError),
123
124 #[error("Parse int error: {0}")]
126 ParseInt(#[from] core::num::ParseIntError),
127
128 #[error("Parse float error: {0}")]
130 ParseFloat(#[from] core::num::ParseFloatError),
131
132 #[error("Parse bool error: {0}")]
134 ParseBool(#[from] core::str::ParseBoolError),
135
136 #[error("Type conversion error: {0}")]
138 ConversionError(compact_str::CompactString),
139
140 #[error("Schema error: {0}")]
142 Schema(compact_str::CompactString),
143
144 #[error("Migration `{tag}` cannot be repaired safely: {reason}")]
147 UnsafeMigrationRepair {
148 tag: CompactString,
150 reason: CompactString,
152 },
153
154 #[error("{adapter} cannot execute this migration: {requirement}")]
157 UnsupportedMigrationExecution {
158 adapter: CompactString,
160 requirement: CompactString,
162 },
163
164 #[error("Database error: {0}")]
166 Other(compact_str::CompactString),
167
168 #[cfg(feature = "rusqlite")]
170 #[error("Rusqlite error: {0}")]
171 Rusqlite(#[from] rusqlite::Error),
172
173 #[cfg(feature = "turso")]
175 #[error("Turso error: {0}")]
176 Turso(#[from] turso::Error),
177
178 #[cfg(feature = "libsql")]
180 #[error("LibSQL error: {0}")]
181 LibSQL(#[from] libsql::Error),
182
183 #[cfg(feature = "tokio-postgres")]
185 #[error("Postgres error: {0}")]
186 Postgres(#[from] tokio_postgres::Error),
187
188 #[cfg(all(feature = "postgres-sync", not(feature = "tokio-postgres")))]
189 #[error("Postgres error: {0}")]
190 Postgres(#[from] postgres::Error),
191
192 #[cfg(feature = "uuid")]
194 #[error("UUID error: {0}")]
195 UuidError(#[from] uuid::Error),
196
197 #[cfg(feature = "serde")]
199 #[error("JSON error: {0}")]
200 JsonError(#[from] serde_json::Error),
201
202 #[error("Infallible conversion error")]
204 Infallible(#[from] core::convert::Infallible),
205}
206
207pub type Result<T> = core::result::Result<T, DrizzleError>;
209
210pub trait ResultExt<T> {
212 fn with_query<F>(self, ctx: F) -> Result<T>
214 where
215 F: FnOnce() -> QueryContext;
216}
217
218impl<T, E> ResultExt<T> for core::result::Result<T, E>
219where
220 E: Into<DrizzleError>,
221{
222 fn with_query<F>(self, ctx: F) -> Result<T>
223 where
224 F: FnOnce() -> QueryContext,
225 {
226 self.map_err(|error| {
227 let source = error.into();
228 match source {
229 DrizzleError::QueryFailed { .. } => source,
230 other => DrizzleError::QueryFailed {
231 ctx: Box::new(ctx()),
232 source: Box::new(other),
233 },
234 }
235 })
236 }
237}