polite/lib.rs
1//! # Polite: SQLite × Polars Integration
2//!
3//! A Rust library that provides seamless integration between SQLite databases
4//! and Polars DataFrames using ConnectorX for efficient data transfer.
5//!
6//! ## Features
7//!
8//! - **Fast data loading**: Use ConnectorX to efficiently load SQLite query results into Polars DataFrames
9//! - **Easy data writing**: Save Polars DataFrames directly to SQLite tables
10//! - **Type preservation**: Automatic mapping between Polars and SQLite data types
11//! - **Simple API**: Minimal boilerplate for common database operations
12//!
13//! ## Quick Start
14//!
15//! ```rust
16//! use polite::{connect_sqlite, to_dataframe, from_dataframe};
17//! use polars::prelude::*;
18//!
19//! // Open a SQLite connection
20//! let conn = connect_sqlite(Some("data.db")).unwrap();
21//!
22//! // Load data from SQLite into a DataFrame
23//! let df = to_dataframe("data.db", "SELECT * FROM users").unwrap();
24//!
25//! // Save a DataFrame to SQLite
26//! from_dataframe(&conn, "new_table", &df).unwrap();
27//! ```
28//!
29//! ## Modules
30//!
31//! - [`dataframe`] - Functions for converting between DataFrames and SQLite
32//! - [`db`] - Database connection utilities
33//! - [`error`] - Custom error types
34
35pub mod dataframe;
36pub mod db;
37pub mod error;
38
39// Re-export the main entrypoints at crate root
40pub use dataframe::{from_dataframe, to_dataframe};
41pub use db::{connect_sqlite, execute_query};
42pub use error::PoliteError;
43
44/// Common imports for polite users.
45///
46/// This prelude module re-exports the most commonly used items from polite,
47/// allowing users to quickly import everything they need with a single glob import.
48///
49/// # Examples
50///
51/// ```rust
52/// use polite::prelude::*;
53///
54/// // Now you have access to all the main functions:
55/// let conn = connect_sqlite(Some("data.db")).unwrap();
56/// let df = to_dataframe("data.db", "SELECT * FROM users").unwrap();
57/// from_dataframe(&conn, "backup_users", &df).unwrap();
58/// ```
59pub mod prelude {
60 pub use crate::{connect_sqlite, execute_query, from_dataframe, to_dataframe, PoliteError};
61
62 // Convenience functions from lib module:
63 pub use crate::{load_dataframe, save_dataframe};
64}
65
66/// Create a DataFrame from a SQLite file with error handling and logging.
67///
68/// This convenience function provides better error messages and optional logging
69/// compared to the raw `to_dataframe` function.
70///
71/// # Arguments
72///
73/// * `db_path` - Path to the SQLite database file
74/// * `sql` - SQL query to execute
75///
76/// # Examples
77///
78/// ```rust
79/// use polite::load_dataframe;
80///
81/// let df = load_dataframe("data.db", "SELECT id, name FROM users LIMIT 10")
82/// .expect("Failed to load data");
83///
84/// println!("Loaded {} rows", df.height());
85/// ```
86pub fn load_dataframe(db_path: &str, sql: &str) -> Result<polars::prelude::DataFrame, PoliteError> {
87 to_dataframe(db_path, sql).map_err(|e| PoliteError::Load {
88 db_path: db_path.to_string(),
89 source: Box::new(e),
90 })
91}
92
93/// Save a DataFrame to SQLite with automatic table creation and better error handling.
94///
95/// This convenience function handles connection creation and provides clearer error messages.
96///
97/// # Arguments
98///
99/// * `db_path` - Path to the SQLite database file (will be created if it doesn't exist)
100/// * `table_name` - Name of the table to create/insert into
101/// * `df` - The DataFrame to save
102///
103/// # Examples
104///
105/// ```rust
106/// use polite::save_dataframe;
107/// use polars::prelude::*;
108///
109/// let df = df! {
110/// "id" => [1, 2, 3],
111/// "name" => ["Alice", "Bob", "Charlie"],
112/// }.unwrap();
113///
114/// save_dataframe("output.db", "users", &df)
115/// .expect("Failed to save DataFrame");
116/// ```
117pub fn save_dataframe(
118 db_path: &str,
119 table_name: &str,
120 df: &polars::prelude::DataFrame,
121) -> Result<(), PoliteError> {
122 let conn = connect_sqlite(Some(db_path))?;
123
124 from_dataframe(&conn, table_name, df).map_err(|e| PoliteError::Save {
125 db_path: db_path.to_string(),
126 table_name: table_name.to_string(),
127 source: Box::new(e),
128 })?;
129
130 Ok(())
131}