#![cfg_attr(feature = "fail-on-warnings", deny(warnings))]
#![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)]
#![allow(clippy::multiple_crate_versions)]
use std::{collections::BTreeMap, sync::Arc};
use async_trait::async_trait;
use switchy_database::Executable;
#[async_trait]
pub trait MutationProvider: Send + Sync {
async fn get_mutation(&self, after_migration_id: &str) -> Option<Arc<dyn Executable>>;
}
#[async_trait]
impl MutationProvider for BTreeMap<String, Arc<dyn Executable>> {
async fn get_mutation(&self, after_migration_id: &str) -> Option<Arc<dyn Executable>> {
self.get(after_migration_id).cloned() }
}
#[async_trait]
impl MutationProvider for Vec<(String, Arc<dyn Executable>)> {
async fn get_mutation(&self, after_migration_id: &str) -> Option<Arc<dyn Executable>> {
self.iter()
.find(|(id, _)| id == after_migration_id)
.map(|(_, executable)| Arc::clone(executable)) }
}
pub struct MutationBuilder {
mutations: Vec<(String, Arc<dyn Executable>)>,
}
impl MutationBuilder {
#[must_use]
pub fn new() -> Self {
Self {
mutations: Vec::new(),
}
}
#[must_use]
pub fn add_mutation<E>(mut self, after_migration_id: &str, mutation: E) -> Self
where
E: Executable + 'static,
{
self.mutations
.push((after_migration_id.to_string(), Arc::new(mutation)));
self
}
#[must_use]
pub fn build(self) -> Vec<(String, Arc<dyn Executable>)> {
self.mutations
}
}
impl Default for MutationBuilder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::{collections::BTreeMap, sync::Arc};
#[switchy_async::test]
async fn test_mutation_builder() {
let mutations = MutationBuilder::new()
.add_mutation("001_test", "SELECT 1")
.add_mutation("002_test", "SELECT 2")
.build();
assert_eq!(mutations.len(), 2);
assert_eq!(mutations[0].0, "001_test");
assert_eq!(mutations[1].0, "002_test");
}
#[switchy_async::test]
async fn test_mutation_builder_default() {
let mutations = MutationBuilder::default().build();
assert_eq!(mutations.len(), 0);
}
#[switchy_async::test]
async fn test_btreemap_mutation_provider() {
let mut mutations = BTreeMap::new();
mutations.insert(
"001_test".to_string(),
Arc::new("SELECT 1".to_string()) as Arc<dyn Executable>,
);
mutations.insert(
"002_test".to_string(),
Arc::new("SELECT 2".to_string()) as Arc<dyn Executable>,
);
let mutation = mutations.get_mutation("001_test").await;
assert!(mutation.is_some());
let no_mutation = mutations.get_mutation("999_nonexistent").await;
assert!(no_mutation.is_none());
}
#[switchy_async::test]
async fn test_vec_mutation_provider() {
let mutations = vec![
(
"001_test".to_string(),
Arc::new("SELECT 1".to_string()) as Arc<dyn Executable>,
),
(
"002_test".to_string(),
Arc::new("SELECT 2".to_string()) as Arc<dyn Executable>,
),
];
let first_mutation = mutations.get_mutation("001_test").await;
assert!(first_mutation.is_some());
let no_mutation = mutations.get_mutation("999_nonexistent").await;
assert!(no_mutation.is_none());
let second_mutation = mutations.get_mutation("002_test").await;
assert!(second_mutation.is_some());
}
#[switchy_async::test]
async fn test_mutation_builder_as_provider() {
let mutations = MutationBuilder::new()
.add_mutation("001_test", "SELECT 1")
.add_mutation("002_test", "SELECT 2")
.build();
let mutation = mutations.get_mutation("001_test").await;
assert!(mutation.is_some());
let no_mutation = mutations.get_mutation("999_nonexistent").await;
assert!(no_mutation.is_none());
}
#[switchy_async::test]
async fn test_mutation_provider_ordering() {
let mut mutations = BTreeMap::new();
mutations.insert(
"003_third".to_string(),
Arc::new("SELECT 3".to_string()) as Arc<dyn Executable>,
);
mutations.insert(
"001_first".to_string(),
Arc::new("SELECT 1".to_string()) as Arc<dyn Executable>,
);
mutations.insert(
"002_second".to_string(),
Arc::new("SELECT 2".to_string()) as Arc<dyn Executable>,
);
let keys: Vec<_> = mutations.keys().collect();
assert_eq!(keys, vec!["001_first", "002_second", "003_third"]);
}
#[switchy_async::test]
async fn test_mutation_provider_multiple_calls() {
let mut mutations = BTreeMap::new();
mutations.insert(
"001_test".to_string(),
Arc::new("SELECT 1".to_string()) as Arc<dyn Executable>,
);
let first_call = mutations.get_mutation("001_test").await;
let second_call = mutations.get_mutation("001_test").await;
assert!(first_call.is_some());
assert!(second_call.is_some());
}
}