use crate::FrameworkError;
use async_trait::async_trait;
#[async_trait]
pub trait Seeder: Send + Sync + 'static {
async fn run(&self) -> Result<(), FrameworkError>;
fn depends_on(&self) -> Vec<&'static str> {
vec![]
}
}
type BoxedSeeder = Box<dyn Seeder>;
type SeederFactory = Box<dyn Fn() -> BoxedSeeder + Send + Sync>;
struct SeederEntry {
name: &'static str,
factory: SeederFactory,
}
pub struct SeederRegistry {
seeders: Vec<SeederEntry>,
}
impl SeederRegistry {
pub fn new() -> Self {
Self {
seeders: Vec::new(),
}
}
pub fn add<S>(mut self) -> Self
where
S: Seeder + Default,
{
let name = std::any::type_name::<S>();
let short_name = name.rsplit("::").next().unwrap_or(name);
self.seeders.push(SeederEntry {
name: Box::leak(short_name.to_string().into_boxed_str()),
factory: Box::new(|| Box::new(S::default())),
});
self
}
pub fn add_instance<S>(mut self, seeder: S) -> Self
where
S: Seeder + Clone + 'static,
{
let name = std::any::type_name::<S>();
let short_name = name.rsplit("::").next().unwrap_or(name);
self.seeders.push(SeederEntry {
name: Box::leak(short_name.to_string().into_boxed_str()),
factory: Box::new(move || Box::new(seeder.clone())),
});
self
}
pub fn names(&self) -> Vec<&'static str> {
self.seeders.iter().map(|e| e.name).collect()
}
pub async fn run_all(&self) -> Result<(), FrameworkError> {
if self.seeders.is_empty() {
println!("No seeders registered.");
return Ok(());
}
println!("Running database seeders...\n");
for entry in &self.seeders {
print!(" Seeding: {}...", entry.name);
let seeder = (entry.factory)();
match seeder.run().await {
Ok(()) => println!(" done"),
Err(e) => {
println!(" FAILED");
return Err(FrameworkError::database(format!(
"Seeder '{}' failed: {}",
entry.name, e
)));
}
}
}
println!("\nSeeding complete!");
Ok(())
}
pub async fn run_one(&self, name: &str) -> Result<(), FrameworkError> {
let entry = self
.seeders
.iter()
.find(|e| e.name == name || e.name.ends_with(&format!("::{name}")))
.ok_or_else(|| {
FrameworkError::internal(format!(
"Seeder '{}' not found. Available: {:?}",
name,
self.names()
))
})?;
println!("Running seeder: {}", entry.name);
let seeder = (entry.factory)();
seeder.run().await?;
println!("Seeder completed!");
Ok(())
}
}
impl Default for SeederRegistry {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
pub trait DatabaseSeeder: Send + Sync {
fn register(&self) -> SeederRegistry;
async fn run(&self) -> Result<(), FrameworkError> {
self.register().run_all().await
}
}