use serde::{Deserialize, Serialize};
use std::sync::Arc;
use crate::{
error::{BifrostError, Result},
registry::TransformRegistry,
transform::{EdgeTransform, Transform, VertexTransform},
};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MappingSpec {
pub version: u32,
pub tables: Vec<TableMapping>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TableMapping {
pub table: String,
#[serde(default)]
pub mode: SyncMode,
pub pipeline: Vec<PipelineStep>,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SyncMode {
#[default]
Full,
Incremental,
Cdc,
}
impl SyncMode {
pub const CHANGE_TYPE_COLUMN: &'static str = "_change_type";
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum PipelineStep {
Vertex {
vertex: VertexTransform,
},
Edge {
edge: EdgeTransform,
},
FkEdges {
fk_edges: EdgeTransform,
},
Custom {
custom: String,
},
}
impl MappingSpec {
pub fn from_yaml(yaml: &str) -> Result<Self> {
Ok(serde_yaml::from_str(yaml)?)
}
pub fn load(path: impl AsRef<std::path::Path>) -> Result<Self> {
Self::from_yaml(&std::fs::read_to_string(path)?)
}
}
impl TableMapping {
pub fn compile(&self, registry: &TransformRegistry) -> Result<Vec<Arc<dyn Transform>>> {
if self.pipeline.is_empty() {
return Err(BifrostError::EmptyPipeline(self.table.clone()));
}
self.pipeline
.iter()
.map(|step| -> Result<Arc<dyn Transform>> {
Ok(match step {
PipelineStep::Vertex { vertex } => Arc::new(vertex.clone()),
PipelineStep::Edge { edge: t } | PipelineStep::FkEdges { fk_edges: t } => {
Arc::new(t.clone())
}
PipelineStep::Custom { custom } => registry.get(custom)?,
})
})
.collect()
}
}