use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use super::pool::DbPool;
use super::DbError;
pub type SeedFuture<'a> = Pin<Box<dyn Future<Output = Result<(), DbError>> + Send + 'a>>;
pub trait Seeder: Send + Sync {
fn name(&self) -> &str;
fn run<'a>(&'a self, pool: &'a DbPool) -> SeedFuture<'a>;
}
struct FnSeeder<F> {
name: String,
run: F,
}
impl<F, Fut> Seeder for FnSeeder<F>
where
F: Fn(DbPool) -> Fut + Send + Sync,
Fut: Future<Output = Result<(), DbError>> + Send + 'static,
{
fn name(&self) -> &str {
&self.name
}
fn run<'a>(&'a self, pool: &'a DbPool) -> SeedFuture<'a> {
Box::pin((self.run)(pool.clone()))
}
}
#[derive(Clone, Default)]
pub struct Seeders {
list: Vec<Arc<dyn Seeder>>,
}
impl std::fmt::Debug for Seeders {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_list().entries(self.names()).finish()
}
}
impl Seeders {
pub fn new() -> Self {
Seeders::default()
}
pub fn add<F, Fut>(self, name: impl Into<String>, run: F) -> Self
where
F: Fn(DbPool) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<(), DbError>> + Send + 'static,
{
self.seeder(FnSeeder { name: name.into(), run })
}
pub fn seeder(mut self, seeder: impl Seeder + 'static) -> Self {
self.list.push(Arc::new(seeder));
self
}
pub fn names(&self) -> Vec<&str> {
self.list.iter().map(|s| s.name()).collect()
}
pub fn len(&self) -> usize {
self.list.len()
}
pub fn is_empty(&self) -> bool {
self.list.is_empty()
}
pub async fn run(&self, pool: &DbPool) -> Result<Vec<String>, DbError> {
let primary = pool.primary();
let mut ran = Vec::with_capacity(self.list.len());
for seeder in &self.list {
seeder.run(primary).await.map_err(|e| DbError::new(format!("seeder '{}' failed: {}", seeder.name(), e.0)))?;
ran.push(seeder.name().to_string());
}
Ok(ran)
}
}
impl DbPool {
pub async fn seed_with(&self, dir: &str, seeders: &Seeders) -> Result<Vec<String>, DbError> {
let mut ran = if seeders.is_empty() || std::path::Path::new(dir).is_dir() { self.seed(dir).await? } else { Vec::new() };
ran.extend(seeders.run(self).await?);
Ok(ran)
}
}