1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
//! # dbcli
//!
//! A utility library that automatically converts SQL query results (`sqlx` Row) into
//! [`serde_json::Value`] without requiring struct mappings.
//!
//! Supports generating JSON arrays directly from database query results, eliminating
//! the need to define structs manually. Suitable for dynamic queries, report exports,
//! and generic API scenarios.
//!
//! ## Supported Databases
//!
//! | Database | Feature flag |
//! |------------|--------------|
//! | MySQL | `mysql` |
//! | PostgreSQL | `postgres` |
//! | SQLite | `sqlite` |
//! | ODBC | `odbc` |
//!
//! ## Feature Flags
//!
//! Enable the corresponding feature in `Cargo.toml` as needed:
//!
//! ```toml
//! [dependencies]
//! dbcli = { path = "...", features = ["postgres"] }
//! # Or enable multiple features at once
//! dbcli = { path = "...", features = ["mysql", "sqlite"] }
//! ```
//!
//! Each feature automatically pulls in the corresponding `sqlx` driver and dependencies
//! such as `chrono` and `rust_decimal`:
//! - `mysql` / `postgres`: includes `chrono`, `rust_decimal`, `base64`, `encoding_rs`
//! - `sqlite`: includes only `base64` (SQLite dates are stored as TEXT, no chrono needed)
//! - `odbc`: includes `odbc-api`, `base64`, `encoding_rs`, `tokio` (requires system unixODBC)
//!
//! ## Quick Usage Example
//!
//! ```rust,no_run
//! use sqlx::PgPool;
//!
//! async fn query_to_json(pool: &PgPool) -> anyhow::Result<()> {
//! use dbcli::to_json::postgres::to_json;
//!
//! let rows = sqlx::query("SELECT id, name, created_at FROM users")
//! .fetch_all(pool)
//! .await?;
//!
//! // data: Vec<serde_json::Value>, each element is a JSON object for one row
//! // columns: Vec<ColumnBaseInfo>, containing column name, type, and index info
//! let (data, columns) = to_json(rows)?;
//! println!("{}", serde_json::to_string_pretty(&data)?);
//! Ok(())
//! }
//! ```
//!
//! ## Custom Parsing Example
//!
//! By implementing the [`to_json::ToJsonCustomizer`] trait, you can override the default
//! conversion logic for specific types:
//!
//! ```rust,no_run
//! use dbcli::to_json::{ToJsonCustomizer, set_to_json_customizer};
//!
//! struct MyCustomizer;
//!
//! impl ToJsonCustomizer for MyCustomizer {
//! #[cfg(feature = "postgres")]
//! fn customize_pg(
//! &self,
//! type_name: &str,
//! ) -> Option<fn(&sqlx::postgres::PgRow, usize) -> serde_json::Value> {
//! match type_name {
//! // Format TIMESTAMP as ISO 8601
//! "TIMESTAMP" => Some(|row, idx| {
//! use sqlx::Row;
//! use chrono::NaiveDateTime;
//! match row.try_get::<Option<NaiveDateTime>, _>(idx) {
//! Ok(Some(dt)) => serde_json::Value::String(
//! dt.format("%Y-%m-%dT%H:%M:%S").to_string()
//! ),
//! _ => serde_json::Value::Null,
//! }
//! }),
//! _ => None,
//! }
//! }
//! }
//!
//! // Register globally once at application startup
//! fn init() {
//! set_to_json_customizer(Box::new(MyCustomizer));
//! }
//! ```
pub use SqlResult;