libsql_orm/types.rs
1//! Type definitions for libsql-orm
2//!
3//! This module contains core type definitions used throughout the library,
4//! including database values, operators, sort orders, and aggregate functions.
5//!
6//! # Core Types
7//!
8//! - [`Value`] - Represents any database value with automatic type conversion
9//! - [`Row`] - Type alias for a database row (HashMap of column names to values)
10//! - [`SortOrder`] - Ascending or descending sort order
11//! - [`Aggregate`] - SQL aggregate functions (COUNT, SUM, AVG, etc.)
12//! - [`JoinType`] - SQL join types (INNER, LEFT, RIGHT, FULL)
13//! - [`Operator`] - SQL comparison operators
14//!
15//! # Examples
16//!
17//! ```rust
18//! use libsql_orm::{Value, SortOrder, Aggregate};
19//!
20//! // Creating values
21//! let text_value = Value::from("hello");
22//! let int_value = Value::from(42i64);
23//! let bool_value = Value::from(true);
24//!
25//! // Using in queries
26//! let sort = SortOrder::Desc;
27//! let agg = Aggregate::Count;
28//! ```
29
30use serde::{Deserialize, Deserializer, Serialize};
31use std::collections::HashMap;
32
33/// Represents a database row as a map of column names to values
34///
35/// This type alias provides a convenient way to work with database rows
36/// as key-value pairs where keys are column names and values are database values.
37pub type Row = HashMap<String, Value>;
38
39/// Represents a database value that can be serialized/deserialized
40///
41/// The `Value` enum covers all possible SQLite/libsql data types and provides
42/// automatic conversion from common Rust types. It supports JSON serialization
43/// for easy data exchange.
44///
45/// # Examples
46///
47/// ```rust
48/// use libsql_orm::Value;
49///
50/// let null_val = Value::Null;
51/// let int_val = Value::Integer(42);
52/// let text_val = Value::Text("hello".to_string());
53/// let bool_val = Value::Boolean(true);
54///
55/// // Automatic conversion
56/// let converted: Value = "hello".into();
57/// let converted: Value = 42i64.into();
58/// let converted: Value = true.into();
59/// ```
60#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
61pub enum Value {
62 Null,
63 Integer(i64),
64 Real(f64),
65 Text(String),
66 Blob(Vec<u8>),
67 Boolean(bool),
68}
69
70impl From<i64> for Value {
71 fn from(v: i64) -> Self {
72 Value::Integer(v)
73 }
74}
75
76impl From<f64> for Value {
77 fn from(v: f64) -> Self {
78 Value::Real(v)
79 }
80}
81
82impl From<String> for Value {
83 fn from(v: String) -> Self {
84 Value::Text(v)
85 }
86}
87
88impl From<&str> for Value {
89 fn from(v: &str) -> Self {
90 Value::Text(v.to_string())
91 }
92}
93
94impl From<bool> for Value {
95 fn from(v: bool) -> Self {
96 Value::Boolean(v)
97 }
98}
99
100impl From<Vec<u8>> for Value {
101 fn from(v: Vec<u8>) -> Self {
102 Value::Blob(v)
103 }
104}
105
106impl From<Option<String>> for Value {
107 fn from(v: Option<String>) -> Self {
108 match v {
109 Some(s) => Value::Text(s),
110 None => Value::Null,
111 }
112 }
113}
114
115impl From<Option<i64>> for Value {
116 fn from(v: Option<i64>) -> Self {
117 match v {
118 Some(i) => Value::Integer(i),
119 None => Value::Null,
120 }
121 }
122}
123
124impl From<Option<f64>> for Value {
125 fn from(v: Option<f64>) -> Self {
126 match v {
127 Some(f) => Value::Real(f),
128 None => Value::Null,
129 }
130 }
131}
132
133impl From<Option<bool>> for Value {
134 fn from(v: Option<bool>) -> Self {
135 match v {
136 Some(b) => Value::Boolean(b),
137 None => Value::Null,
138 }
139 }
140}
141
142impl From<Option<Vec<u8>>> for Value {
143 fn from(v: Option<Vec<u8>>) -> Self {
144 match v {
145 Some(b) => Value::Blob(b),
146 None => Value::Null,
147 }
148 }
149}
150
151impl From<serde_json::Value> for Value {
152 fn from(v: serde_json::Value) -> Self {
153 match v {
154 serde_json::Value::Null => Value::Null,
155 serde_json::Value::Bool(b) => Value::Boolean(b),
156 serde_json::Value::Number(n) => {
157 if let Some(i) = n.as_i64() {
158 Value::Integer(i)
159 } else if let Some(f) = n.as_f64() {
160 Value::Real(f)
161 } else {
162 Value::Text(n.to_string())
163 }
164 }
165 serde_json::Value::String(s) => Value::Text(s),
166 serde_json::Value::Array(_) | serde_json::Value::Object(_) => {
167 Value::Text(v.to_string())
168 }
169 }
170 }
171}
172
173/// Sort order for queries
174///
175/// Specifies whether query results should be sorted in ascending or descending order.
176///
177/// # Examples
178///
179/// ```rust
180/// use libsql_orm::{SortOrder, Sort};
181///
182/// let asc_sort = Sort::new("name", SortOrder::Asc);
183/// let desc_sort = Sort::new("created_at", SortOrder::Desc);
184/// ```
185#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)]
186pub enum SortOrder {
187 #[default]
188 Asc,
189 Desc,
190}
191
192impl std::fmt::Display for SortOrder {
193 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
194 match self {
195 SortOrder::Asc => write!(f, "ASC"),
196 SortOrder::Desc => write!(f, "DESC"),
197 }
198 }
199}
200
201/// Aggregate functions
202///
203/// SQL aggregate functions for performing calculations on sets of values.
204///
205/// # Examples
206///
207/// ```rust
208/// use libsql_orm::{Model, Aggregate};
209///
210/// // Count all users
211/// let count = User::aggregate(Aggregate::Count, "*", None, &db).await?;
212///
213/// // Average age
214/// let avg_age = User::aggregate(Aggregate::Avg, "age", None, &db).await?;
215///
216/// // Maximum salary
217/// let max_salary = User::aggregate(Aggregate::Max, "salary", None, &db).await?;
218/// ```
219#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
220pub enum Aggregate {
221 Count,
222 Sum,
223 Avg,
224 Min,
225 Max,
226}
227
228impl std::fmt::Display for Aggregate {
229 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
230 match self {
231 Aggregate::Count => write!(f, "COUNT"),
232 Aggregate::Sum => write!(f, "SUM"),
233 Aggregate::Avg => write!(f, "AVG"),
234 Aggregate::Min => write!(f, "MIN"),
235 Aggregate::Max => write!(f, "MAX"),
236 }
237 }
238}
239
240/// Join types for queries
241///
242/// SQL join types for combining data from multiple tables.
243///
244/// # Examples
245///
246/// ```rust
247/// use libsql_orm::JoinType;
248///
249/// let inner = JoinType::Inner; // INNER JOIN
250/// let left = JoinType::Left; // LEFT JOIN
251/// let right = JoinType::Right; // RIGHT JOIN
252/// let full = JoinType::Full; // FULL JOIN
253/// ```
254#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
255pub enum JoinType {
256 Inner,
257 Left,
258 Right,
259 Full,
260}
261
262impl std::fmt::Display for JoinType {
263 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
264 match self {
265 JoinType::Inner => write!(f, "INNER JOIN"),
266 JoinType::Left => write!(f, "LEFT JOIN"),
267 JoinType::Right => write!(f, "RIGHT JOIN"),
268 JoinType::Full => write!(f, "FULL JOIN"),
269 }
270 }
271}
272
273/// SQL operator types
274///
275/// Comparison and logical operators for use in WHERE clauses and filters.
276///
277/// # Examples
278///
279/// ```rust
280/// use libsql_orm::{Operator, FilterOperator, Value};
281///
282/// // Equal comparison
283/// let filter = FilterOperator::Eq("status".to_string(), Value::Text("active".to_string()));
284///
285/// // Greater than
286/// let filter = FilterOperator::Gt("age".to_string(), Value::Integer(18));
287///
288/// // LIKE pattern matching
289/// let filter = FilterOperator::Like("name".to_string(), Value::Text("%john%".to_string()));
290/// ```
291#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
292pub enum Operator {
293 Eq,
294 Ne,
295 Lt,
296 Le,
297 Gt,
298 Ge,
299 Like,
300 NotLike,
301 In,
302 NotIn,
303 IsNull,
304 IsNotNull,
305 Between,
306 NotBetween,
307}
308
309impl std::fmt::Display for Operator {
310 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
311 match self {
312 Operator::Eq => write!(f, "="),
313 Operator::Ne => write!(f, "!="),
314 Operator::Lt => write!(f, "<"),
315 Operator::Le => write!(f, "<="),
316 Operator::Gt => write!(f, ">"),
317 Operator::Ge => write!(f, ">="),
318 Operator::Like => write!(f, "LIKE"),
319 Operator::NotLike => write!(f, "NOT LIKE"),
320 Operator::In => write!(f, "IN"),
321 Operator::NotIn => write!(f, "NOT IN"),
322 Operator::IsNull => write!(f, "IS NULL"),
323 Operator::IsNotNull => write!(f, "IS NOT NULL"),
324 Operator::Between => write!(f, "BETWEEN"),
325 Operator::NotBetween => write!(f, "NOT BETWEEN"),
326 }
327 }
328}
329
330/// Custom deserializer for boolean fields that handles SQLite integer conversion
331///
332/// SQLite stores boolean values as integers (0 for false, 1 for true).
333/// This deserializer automatically converts these integers to proper Rust boolean types.
334///
335/// # Usage
336///
337/// ```rust
338/// use libsql_orm::deserialize_bool;
339/// use serde::{Deserialize, Serialize};
340///
341/// #[derive(Serialize, Deserialize)]
342/// struct User {
343/// pub id: Option<i64>,
344/// pub name: String,
345/// #[serde(deserialize_with = "deserialize_bool")]
346/// pub is_active: bool,
347/// }
348/// ```
349pub fn deserialize_bool<'de, D>(deserializer: D) -> Result<bool, D::Error>
350where
351 D: Deserializer<'de>,
352{
353 use serde::de::Error;
354
355 let value = serde_json::Value::deserialize(deserializer)?;
356 match value {
357 serde_json::Value::Bool(b) => Ok(b),
358 serde_json::Value::Number(n) => {
359 if let Some(i) = n.as_i64() {
360 Ok(i != 0)
361 } else if let Some(f) = n.as_f64() {
362 Ok(f != 0.0)
363 } else {
364 Err(Error::custom("Invalid number format for boolean"))
365 }
366 }
367 serde_json::Value::String(s) => match s.to_lowercase().as_str() {
368 "true" | "1" | "yes" | "on" => Ok(true),
369 "false" | "0" | "no" | "off" => Ok(false),
370 _ => Err(Error::custom(format!(
371 "Invalid string value for boolean: {s}"
372 ))),
373 },
374 _ => Err(Error::custom("Expected boolean, integer, or string")),
375 }
376}