1pub mod backend;
38pub mod connection;
39pub mod db_schema;
40pub mod eager;
41pub mod migration;
42pub mod pool;
43pub mod query;
44pub mod relation;
45pub mod repository;
46pub mod seed;
47pub mod timestamp;
48mod decode;
49pub mod uuid;
50#[cfg(test)]
51mod tests;
52#[cfg(test)]
53mod backends;
54#[cfg(test)]
55mod convention_tests;
56#[cfg(test)]
57mod part2_tests;
58#[cfg(test)]
59mod tls_tests;
60
61pub use backend::Backend;
62pub use connection::{DbConfig, DbSslMode, DbTls};
63pub use migration::MigrationStatus;
64pub use db_schema::{ColumnSchema, DbSchema, ForeignKeySchema, IndexSchema, TableSchema};
65pub use eager::{EagerQuery, Loaded, Relation, RelationKind};
66pub use pool::{DbPool, DbTransaction, Replicas};
67pub use query::{Order, QueryBuilder};
68pub use relation::{BelongsTo, BelongsToMany, HasMany, HasOne, SyncChanges};
69pub use seed::{Seeder, Seeders};
70pub use repository::{ModelRepository, PrimaryKey, Repository};
71pub use timestamp::Timestamp;
72pub use uuid::Uuid;
73pub use crate::pagination::{CursorPage, Page};
74
75#[derive(Debug, Clone, PartialEq)]
79pub enum Value {
80 Null,
81 Bool(bool),
82 Int(i64),
83 Float(f64),
84 Text(String),
85 Bytes(Vec<u8>),
86}
87
88#[derive(Debug)]
98pub struct DbError(pub String);
99
100impl std::fmt::Display for DbError {
101 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102 write!(f, "DbError: {}", self.0)
103 }
104}
105
106impl std::error::Error for DbError {}
107
108#[derive(Debug, Clone, Copy, PartialEq, Eq)]
111#[non_exhaustive]
112pub enum DbErrorKind {
113 StaleVersion,
117 NotFound,
119 Other,
121}
122
123const STALE_VERSION: &str = "stale version: ";
124const NOT_FOUND: &str = "not found: ";
125
126impl DbError {
127 pub fn new(msg: impl Into<String>) -> Self {
128 DbError(msg.into())
129 }
130
131 pub fn stale_version(detail: impl std::fmt::Display) -> Self {
134 DbError(format!("{STALE_VERSION}{detail}"))
135 }
136
137 pub fn not_found(detail: impl std::fmt::Display) -> Self {
139 DbError(format!("{NOT_FOUND}{detail}"))
140 }
141
142 pub fn kind(&self) -> DbErrorKind {
153 if self.0.starts_with(STALE_VERSION) {
154 DbErrorKind::StaleVersion
155 } else if self.0.starts_with(NOT_FOUND) {
156 DbErrorKind::NotFound
157 } else {
158 DbErrorKind::Other
159 }
160 }
161
162 pub fn is_stale_version(&self) -> bool {
164 self.kind() == DbErrorKind::StaleVersion
165 }
166
167 pub fn is_not_found(&self) -> bool {
169 self.kind() == DbErrorKind::NotFound
170 }
171}
172
173#[derive(Debug, Clone)]
177pub struct ModelRow {
178 #[doc(hidden)]
179 pub columns: Vec<(String, Value)>,
180}
181
182impl ModelRow {
183 pub fn new(columns: Vec<(String, Value)>) -> Self {
184 ModelRow { columns }
185 }
186
187 pub fn get<T: FromColumn>(&self, col: &str) -> Result<T, DbError> {
189 for (name, val) in &self.columns {
190 if name.eq_ignore_ascii_case(col) {
191 return T::from_column(val.clone());
192 }
193 }
194 Err(DbError::new(format!("column '{}' not found in row", col)))
195 }
196}
197
198pub trait FromColumn: Sized {
202 fn from_column(v: Value) -> Result<Self, DbError>;
203}
204
205pub trait ToColumn {
207 fn to_column(&self) -> Value;
208}
209
210impl FromColumn for i16 {
213 fn from_column(v: Value) -> Result<Self, DbError> {
214 match v {
215 Value::Int(n) => Ok(n as i16),
216 Value::Null => Err(DbError::new("unexpected NULL for i16")),
217 other => Err(DbError::new(format!("cannot convert {:?} to i16", other))),
218 }
219 }
220}
221
222impl FromColumn for i32 {
223 fn from_column(v: Value) -> Result<Self, DbError> {
224 match v {
225 Value::Int(n) => Ok(n as i32),
226 Value::Null => Err(DbError::new("unexpected NULL for i32")),
227 other => Err(DbError::new(format!("cannot convert {:?} to i32", other))),
228 }
229 }
230}
231
232impl FromColumn for i64 {
233 fn from_column(v: Value) -> Result<Self, DbError> {
234 match v {
235 Value::Int(n) => Ok(n),
236 Value::Null => Err(DbError::new("unexpected NULL for i64")),
237 other => Err(DbError::new(format!("cannot convert {:?} to i64", other))),
238 }
239 }
240}
241
242impl FromColumn for u32 {
243 fn from_column(v: Value) -> Result<Self, DbError> {
244 match v {
245 Value::Int(n) => Ok(n as u32),
246 Value::Null => Err(DbError::new("unexpected NULL for u32")),
247 other => Err(DbError::new(format!("cannot convert {:?} to u32", other))),
248 }
249 }
250}
251
252impl FromColumn for u64 {
253 fn from_column(v: Value) -> Result<Self, DbError> {
254 match v {
255 Value::Int(n) => Ok(n as u64),
256 Value::Null => Err(DbError::new("unexpected NULL for u64")),
257 other => Err(DbError::new(format!("cannot convert {:?} to u64", other))),
258 }
259 }
260}
261
262impl FromColumn for f32 {
263 fn from_column(v: Value) -> Result<Self, DbError> {
264 match v {
265 Value::Float(f) => Ok(f as f32),
266 Value::Int(n) => Ok(n as f32),
267 Value::Null => Err(DbError::new("unexpected NULL for f32")),
268 other => Err(DbError::new(format!("cannot convert {:?} to f32", other))),
269 }
270 }
271}
272
273impl FromColumn for f64 {
274 fn from_column(v: Value) -> Result<Self, DbError> {
275 match v {
276 Value::Float(f) => Ok(f),
277 Value::Int(n) => Ok(n as f64),
278 Value::Null => Err(DbError::new("unexpected NULL for f64")),
279 other => Err(DbError::new(format!("cannot convert {:?} to f64", other))),
280 }
281 }
282}
283
284impl FromColumn for bool {
285 fn from_column(v: Value) -> Result<Self, DbError> {
286 match v {
287 Value::Bool(b) => Ok(b),
288 Value::Int(n) => Ok(n != 0),
289 Value::Null => Err(DbError::new("unexpected NULL for bool")),
290 other => Err(DbError::new(format!("cannot convert {:?} to bool", other))),
291 }
292 }
293}
294
295impl FromColumn for String {
296 fn from_column(v: Value) -> Result<Self, DbError> {
297 match v {
298 Value::Text(s) => Ok(s),
299 Value::Null => Err(DbError::new("unexpected NULL for String")),
300 other => Err(DbError::new(format!("cannot convert {:?} to String", other))),
301 }
302 }
303}
304
305impl FromColumn for Value {
307 fn from_column(v: Value) -> Result<Self, DbError> {
308 Ok(v)
309 }
310}
311
312impl<T: FromColumn> FromColumn for Option<T> {
313 fn from_column(v: Value) -> Result<Self, DbError> {
314 match v {
315 Value::Null => Ok(None),
316 other => Ok(Some(T::from_column(other)?)),
317 }
318 }
319}
320
321impl ToColumn for i16 {
324 fn to_column(&self) -> Value { Value::Int(*self as i64) }
325}
326impl ToColumn for i32 {
327 fn to_column(&self) -> Value { Value::Int(*self as i64) }
328}
329impl ToColumn for i64 {
330 fn to_column(&self) -> Value { Value::Int(*self) }
331}
332impl ToColumn for u32 {
333 fn to_column(&self) -> Value { Value::Int(*self as i64) }
334}
335impl ToColumn for u64 {
336 fn to_column(&self) -> Value { Value::Int(*self as i64) }
337}
338impl ToColumn for f32 {
339 fn to_column(&self) -> Value { Value::Float(*self as f64) }
340}
341impl ToColumn for f64 {
342 fn to_column(&self) -> Value { Value::Float(*self) }
343}
344impl ToColumn for bool {
345 fn to_column(&self) -> Value { Value::Bool(*self) }
346}
347impl ToColumn for String {
348 fn to_column(&self) -> Value { Value::Text(self.clone()) }
349}
350impl ToColumn for str {
351 fn to_column(&self) -> Value { Value::Text(self.to_owned()) }
352}
353impl ToColumn for &str {
354 fn to_column(&self) -> Value { Value::Text((*self).to_owned()) }
355}
356impl<T: ToColumn> ToColumn for Option<T> {
357 fn to_column(&self) -> Value {
358 match self {
359 Some(v) => v.to_column(),
360 None => Value::Null,
361 }
362 }
363}
364
365pub trait Model: Sized + Send + Sync {
369 fn table_name() -> &'static str;
371
372 fn column_names() -> &'static [&'static str];
374
375 fn primary_key_name() -> &'static str;
377
378 fn primary_key_value(&self) -> Value;
380
381 fn primary_key_auto_increment() -> bool {
383 false
384 }
385
386 fn from_row(row: &ModelRow) -> Result<Self, DbError>;
388
389 fn to_values(&self) -> Vec<(&'static str, Value)>;
392
393 fn created_at_column() -> Option<&'static str> {
396 None
397 }
398
399 fn updated_at_column() -> Option<&'static str> {
402 None
403 }
404
405 fn soft_delete_column() -> Option<&'static str> {
409 None
410 }
411
412 fn version_column() -> Option<&'static str> {
416 None
417 }
418}
419
420#[derive(Clone, Debug, PartialEq, Eq)]
431pub struct ModelInfo {
432 pub type_name: &'static str,
434 pub table: &'static str,
436 pub columns: &'static [&'static str],
438 pub primary_key: &'static str,
440 pub auto_increment: bool,
442}
443
444impl ModelInfo {
445 pub fn of<T: Model>() -> Self {
447 ModelInfo {
448 type_name: std::any::type_name::<T>(),
449 table: T::table_name(),
450 columns: T::column_names(),
451 primary_key: T::primary_key_name(),
452 auto_increment: T::primary_key_auto_increment(),
453 }
454 }
455
456 pub fn short_name(&self) -> &'static str {
459 let base = self.type_name.split('<').next().unwrap_or(self.type_name);
460 base.rsplit("::").next().unwrap_or(base)
461 }
462}