use super::{Extractor, Loader, Transformer};
use crate::Result;
pub struct Pipeline<E, T, L> {
extractor: E,
transformer: T,
loader: L,
}
impl<E, T, L> Pipeline<E, T, L>
where
E: Extractor,
T: Transformer<Input = E::Item>,
L: Loader<Item = T::Output>,
{
pub fn new(extractor: E, transformer: T, loader: L) -> Self {
Self {
extractor,
transformer,
loader,
}
}
pub async fn run(&self) -> Result<usize> {
tracing::info!("Starting ETL pipeline");
tracing::debug!("Extracting from source...");
let items = self.extractor.extract().await?;
tracing::info!("Extracted {} items", items.len());
if items.is_empty() {
tracing::warn!("No items extracted, pipeline complete");
return Ok(0);
}
tracing::debug!("Transforming items...");
let transformed = self.transformer.transform_many(items)?;
tracing::info!("Transformed {} items", transformed.len());
tracing::debug!("Loading to destination...");
let count = self.loader.load(transformed).await?;
tracing::info!("Loaded {} items", count);
Ok(count)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Result;
struct MockExtractor(Vec<i32>);
impl Extractor for MockExtractor {
type Item = i32;
async fn extract(&self) -> Result<Vec<Self::Item>> {
Ok(self.0.clone())
}
}
struct DoubleTransformer;
impl Transformer for DoubleTransformer {
type Input = i32;
type Output = i32;
fn transform(&self, input: Self::Input) -> Result<Self::Output> {
Ok(input * 2)
}
}
struct SumLoader(std::sync::Arc<std::sync::Mutex<i32>>);
impl Loader for SumLoader {
type Item = i32;
async fn load(&self, items: Vec<Self::Item>) -> Result<usize> {
let sum: i32 = items.iter().sum();
*self.0.lock().unwrap() = sum;
Ok(items.len())
}
}
#[tokio::test]
async fn test_pipeline() {
let result = std::sync::Arc::new(std::sync::Mutex::new(0));
let pipeline = Pipeline::new(
MockExtractor(vec![1, 2, 3]),
DoubleTransformer,
SumLoader(result.clone()),
);
let count = pipeline.run().await.unwrap();
assert_eq!(count, 3);
assert_eq!(*result.lock().unwrap(), 12); }
#[tokio::test]
async fn test_empty_pipeline() {
let result = std::sync::Arc::new(std::sync::Mutex::new(0));
let pipeline = Pipeline::new(
MockExtractor(vec![]),
DoubleTransformer,
SumLoader(result.clone()),
);
let count = pipeline.run().await.unwrap();
assert_eq!(count, 0);
}
}