use super::sql_script::{load_database_url, split_sql_statements};
use std::fs;
use std::path::Path;
pub fn create_seeder(name: &str) -> Result<(), Box<dyn std::error::Error>> {
let seeds_dir = Path::new("seeds");
if !seeds_dir.exists() {
fs::create_dir(seeds_dir)?;
}
let timestamp = chrono::Utc::now().format("%Y%m%d%H%M%S");
let filename = format!("{}_{}.sql", timestamp, name.to_lowercase());
let filepath = seeds_dir.join(&filename);
let template = format!(
r#"-- Seed: {}
-- Created at: {}
-- Generated by `oxidite generate seeder`.
-- Add deterministic seed data here so it can be rerun safely in development.
-- Replace the example insert below with the records your app actually needs.
INSERT INTO users (username, email) VALUES ('admin', 'admin@example.com');
"#,
name,
chrono::Utc::now().to_rfc3339()
);
fs::write(&filepath, template)?;
println!("✅ Created seeder: {}", filepath.display());
println!("\nEdit the file to add your seed data.");
Ok(())
}
pub async fn run_seeders() -> Result<(), Box<dyn std::error::Error>> {
use oxidite_db::{Database, DbPool};
let db_url = load_database_url()?;
let db = DbPool::connect(&db_url).await?;
let seeds_dir = Path::new("seeds");
if !seeds_dir.exists() {
println!("No seeds directory found.");
return Ok(());
}
let mut seed_files: Vec<_> = fs::read_dir(seeds_dir)?
.filter_map(|entry| entry.ok())
.filter(|entry| {
entry
.path()
.extension()
.and_then(|s| s.to_str())
.map(|s| s == "sql")
.unwrap_or(false)
})
.collect();
seed_files.sort_by_key(|entry| entry.file_name());
if seed_files.is_empty() {
println!("No seed files found.");
return Ok(());
}
println!("Running {} seeders...\n", seed_files.len());
for entry in seed_files {
let path = entry.path();
let filename = path.file_name().unwrap().to_string_lossy();
println!("🌱 Seeding: {}", filename);
let sql = fs::read_to_string(&path)?;
if !sql.trim().is_empty() {
for statement in split_sql_statements(&sql) {
db.execute(&statement).await?;
}
println!(" ✅ Done");
} else {
println!(" ⚠️ Empty seeder");
}
}
println!("\n✅ All seeders run successfully!");
Ok(())
}
#[cfg(test)]
mod tests {
use crate::commands::sql_script::split_sql_statements;
#[test]
fn sql_split_ignores_comments() {
let sql = r#"
-- start
INSERT INTO users (id) VALUES (1);
-- another
INSERT INTO users (id) VALUES (2);
"#;
let statements = split_sql_statements(sql);
assert_eq!(statements.len(), 2);
assert!(statements[0].contains("VALUES (1)"));
assert!(statements[1].contains("VALUES (2)"));
}
}