use std::collections::HashMap;
use std::sync::Arc;
use async_trait::async_trait;
use parking_lot::RwLock;
use crate::container::Container;
use crate::Error;
#[async_trait]
pub trait Seeder: Send + Sync {
fn name(&self) -> &'static str {
std::any::type_name::<Self>()
}
async fn run(&self, c: &Container) -> Result<(), Error>;
}
pub type BoxedSeeder = Box<dyn Seeder>;
pub struct SeederRegistration {
pub name: &'static str,
pub builder: fn() -> Arc<dyn Seeder>,
}
inventory::collect!(SeederRegistration);
pub fn collected() -> Vec<(&'static str, Arc<dyn Seeder>)> {
inventory::iter::<SeederRegistration>
.into_iter()
.map(|r| (r.name, (r.builder)()))
.collect()
}
pub async fn call_seeders(c: &Container, seeders: &[BoxedSeeder]) -> Result<(), Error> {
for s in seeders {
tracing::info!(seeder = %s.name(), "running");
s.run(c).await?;
}
Ok(())
}
#[derive(Default, Clone)]
pub struct SeederRegistry {
inner: Arc<RwLock<HashMap<String, Arc<dyn Seeder>>>>,
}
impl SeederRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn from_inventory() -> Self {
let registry = Self::new();
for (name, seeder) in collected() {
registry.inner.write().insert(name.to_string(), seeder);
}
registry
}
pub fn register<S: Seeder + 'static>(&self, name: impl Into<String>, seeder: S) {
self.inner.write().insert(name.into(), Arc::new(seeder));
}
pub fn get(&self, name: &str) -> Option<Arc<dyn Seeder>> {
self.inner.read().get(name).cloned()
}
pub fn names(&self) -> Vec<String> {
let mut names: Vec<String> = self.inner.read().keys().cloned().collect();
names.sort();
names
}
pub async fn run(&self, c: &Container, name: &str) -> Result<(), Error> {
let seeder = self
.get(name)
.ok_or_else(|| Error::Internal(format!("unknown seeder: {name}")))?;
seeder.run(c).await
}
}
pub trait Factory<M>: Sized {
fn definition() -> M;
fn make_many(count: usize) -> Vec<M> {
(0..count).map(|_| Self::definition()).collect()
}
}
#[async_trait]
pub trait PersistentFactory<M>: Factory<M>
where
M: Send,
{
async fn save(c: &Container, model: M) -> Result<M, Error>;
async fn create(c: &Container) -> Result<M, Error> {
Self::save(c, Self::definition()).await
}
async fn create_many(c: &Container, count: usize) -> Result<Vec<M>, Error> {
let mut out = Vec::with_capacity(count);
for instance in Self::make_many(count) {
out.push(Self::save(c, instance).await?);
}
Ok(out)
}
}
pub trait HasFactory: Sized {
type Factory: Factory<Self>;
fn factory() -> FactoryBuilder<Self, Self::Factory> {
FactoryBuilder::new()
}
}
pub struct FactoryBuilder<M, F: Factory<M>> {
count: usize,
_phantom: std::marker::PhantomData<(M, F)>,
}
impl<M, F: Factory<M>> FactoryBuilder<M, F> {
pub fn new() -> Self {
Self {
count: 1,
_phantom: std::marker::PhantomData,
}
}
pub fn count(mut self, n: usize) -> Self {
self.count = n;
self
}
pub fn make(self) -> Vec<M> {
F::make_many(self.count)
}
pub async fn create(self, c: &Container) -> Result<Vec<M>, Error>
where
M: Send,
F: PersistentFactory<M> + Send,
{
F::create_many(c, self.count).await
}
pub async fn create_one(self, c: &Container) -> Result<M, Error>
where
M: Send,
F: PersistentFactory<M> + Send,
{
F::create(c).await
}
}
impl<M, F: Factory<M>> Default for FactoryBuilder<M, F> {
fn default() -> Self {
Self::new()
}
}