Created
June 1, 2025 16:17
-
-
Save lxl66566/85de8095cd6644396a901440af2e10f8 to your computer and use it in GitHub Desktop.
Automatically build sqlite database for compiling sqlx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//! The entire build.rs does one thing: applies schema.sql to | |
//! target/sqlx_schema.db for sqlx to use during compilation. | |
use std::process; // For panic | |
use std::{env, fs, path::PathBuf}; | |
// build.rs runs in a sync context, so we need a runtime to run sqlx async functions | |
use fuck_backslash::FuckBackslash; | |
use path_absolutize::Absolutize; | |
// We need the execute trait from sqlx prelude | |
use sqlx::Executor; | |
// Import sqlx types needed | |
use sqlx::{Connection, SqliteConnection}; /* Use Connection trait and specific | |
* SqliteConnection */ | |
use tokio::runtime::Runtime; | |
fn main() { | |
// Run the async setup function using a tokio runtime | |
let rt = Runtime::new().expect("Failed to create Tokio runtime for build script"); | |
if let Err(e) = rt.block_on(setup_schema_db()) { | |
eprintln!("Error during build script database setup: {}", e); | |
process::exit(1); // Signal build failure | |
} | |
} | |
async fn setup_schema_db() -> Result<(), Box<dyn std::error::Error>> { | |
println!("cargo:rerun-if-changed=schema.sql"); | |
println!("cargo:rerun-if-changed=build.rs"); | |
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR")?); | |
let target_dir = manifest_dir.join("target"); | |
let db_filename = "sqlx_schema.db"; // Consistent name | |
let db_path = target_dir.join(db_filename); | |
let schema_path = manifest_dir.join("schema.sql"); | |
// Ensure target directory exists | |
if !target_dir.exists() { | |
fs::create_dir_all(&target_dir)?; | |
println!( | |
"cargo:warning=Created target directory: {}", | |
target_dir.display() | |
); | |
} | |
// Construct the connection URL for creation (needs to allow creation) | |
// Note: Using canonicalize here might fail if the file doesn't exist yet. | |
// We build the URL based on the *intended* absolute path first. | |
// For Windows, paths need careful handling, canonicalize helps later. | |
let db_url_for_creation = format!("sqlite:{}?mode=rwc", db_path.display()); // mode=rwc (ReadWriteCreate) | |
// Only create and setup if the database file doesn't exist | |
if !db_path.exists() { | |
println!( | |
"cargo:warning=Schema DB file {} not found. Creating and initializing from {}.", | |
db_path.display(), | |
schema_path.display() | |
); | |
if !schema_path.exists() { | |
// Use panic! in build scripts for fatal errors that should stop the build | |
panic!("schema.sql not found at {}", schema_path.display()); | |
} | |
// Read schema.sql | |
let schema_sql = fs::read_to_string(&schema_path)?; | |
// Connect using sqlx (this will create the file due to mode=rwc) | |
let mut conn = match SqliteConnection::connect(&db_url_for_creation).await { | |
Ok(c) => c, | |
Err(e) => { | |
panic!( | |
"Failed to connect to (create) SQLite database at '{}': {}", | |
db_url_for_creation, | |
e // Use the creation URL in error msg | |
); | |
} | |
}; | |
// Execute the schema script | |
match conn.execute(&*schema_sql).await { | |
// Pass schema_sql as &str | |
Ok(_) => { | |
println!( | |
"cargo:warning=Successfully created and initialized schema DB: {}", | |
db_path.display() | |
); | |
} | |
Err(e) => { | |
// Attempt to clean up the partially created file on error | |
println!( | |
"cargo:warning=Failed to execute schema SQL. Attempting to remove partially created DB file: {}", | |
db_path.display() | |
); | |
let _ = fs::remove_file(&db_path); // Ignore error on removal | |
panic!( | |
"Failed to execute schema SQL from {}: {}\nSQL:\n{}", | |
schema_path.display(), | |
e, | |
schema_sql | |
); | |
} | |
}; | |
// Connection is closed when `conn` goes out of scope | |
} else { | |
println!( | |
"cargo:warning=Schema DB file {} already exists. Skipping creation.", | |
db_path.display() | |
); | |
} | |
// --- Set DATABASE_URL environment variable for sqlx macros --- | |
// Now that the file is guaranteed to exist, get its canonical path. | |
let absolute_db_path = match db_path.absolutize() { | |
Ok(p) => p.into_owned().fuck_backslash(), | |
Err(e) => panic!( | |
"Failed to get canonical path for {}: {}", | |
db_path.display(), | |
e | |
), | |
}; | |
let db_path_str = absolute_db_path | |
.to_str() | |
.ok_or("DB path is not valid UTF-8")?; | |
// Construct the final DATABASE_URL for compile-time checks. | |
// It does NOT need mode=rwc, just the path. | |
// IMPORTANT: Use the `sqlite://` prefix for sqlx compile-time checks! | |
let database_url_for_macros = format!("sqlite://{}", db_path_str); | |
// Set the environment variable | |
println!("cargo:rustc-env=DATABASE_URL={}", database_url_for_macros); | |
println!( | |
"cargo:warning=DATABASE_URL for compile-time checks set to: {}", | |
database_url_for_macros | |
); | |
Ok(()) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment