use std::fmt;
use crate::{
MssqlTargetConfig, MssqlTargetOutputPlan, PhaseTimingReport, ResolvedMssqlTarget,
support::sanitize_text_for_display,
};
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum RunMode {
#[default]
Execute,
DryRun,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LazyTable {
id: LazyTableId,
kind: LazyTableKind,
name: String,
}
impl LazyTable {
#[cfg(test)]
#[must_use]
pub(crate) fn placeholder(id: u64, kind: LazyTableKind) -> Self {
Self {
id: LazyTableId(id),
kind,
name: format!("table_{id}"),
}
}
pub(super) fn delta_source(id: u64, name: String) -> Self {
Self {
id: LazyTableId(id),
kind: LazyTableKind::DeltaSource,
name,
}
}
pub(super) fn derived_sql(id: u64) -> Self {
Self {
id: LazyTableId(id),
kind: LazyTableKind::DerivedSql,
name: format!("table_{id}"),
}
}
pub(super) fn with_name(&self, name: String) -> Self {
Self {
id: self.id,
kind: self.kind,
name,
}
}
#[must_use]
pub const fn id(&self) -> u64 {
self.id.0
}
#[must_use]
pub const fn kind(&self) -> LazyTableKind {
self.kind
}
#[must_use]
pub fn name(&self) -> &str {
&self.name
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct LazyTableId(u64);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LazyTableKind {
DeltaSource,
DerivedSql,
}
#[derive(Clone, PartialEq, Eq)]
pub struct MssqlOutputTarget {
output_name: String,
target: MssqlTargetConfig,
run_mode: RunMode,
}
impl MssqlOutputTarget {
#[must_use]
pub fn new(
output_name: impl Into<String>,
target: MssqlTargetConfig,
run_mode: RunMode,
) -> Self {
Self {
output_name: output_name.into(),
target,
run_mode,
}
}
#[must_use]
pub fn output_name(&self) -> &str {
&self.output_name
}
#[must_use]
pub const fn target(&self) -> &MssqlTargetConfig {
&self.target
}
#[must_use]
pub const fn run_mode(&self) -> RunMode {
self.run_mode
}
}
impl fmt::Debug for MssqlOutputTarget {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("MssqlOutputTarget")
.field("output_name", &sanitize_text_for_display(&self.output_name))
.field("target", &self.target)
.field("run_mode", &self.run_mode)
.finish()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OutputWritePlan {
table: LazyTable,
target: MssqlOutputTarget,
}
impl OutputWritePlan {
#[must_use]
pub const fn new(table: LazyTable, target: MssqlOutputTarget) -> Self {
Self { table, target }
}
#[must_use]
pub const fn table(&self) -> &LazyTable {
&self.table
}
#[must_use]
pub const fn target(&self) -> &MssqlOutputTarget {
&self.target
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PlannedMssqlOutput {
request: OutputWritePlan,
resolved_target: ResolvedMssqlTarget,
output_plan: MssqlTargetOutputPlan,
phase_timings: Vec<PhaseTimingReport>,
}
impl PlannedMssqlOutput {
pub(super) fn new(
request: OutputWritePlan,
resolved_target: ResolvedMssqlTarget,
output_plan: MssqlTargetOutputPlan,
phase_timings: Vec<PhaseTimingReport>,
) -> Self {
Self {
request,
resolved_target,
output_plan,
phase_timings,
}
}
#[must_use]
pub const fn request(&self) -> &OutputWritePlan {
&self.request
}
#[must_use]
pub const fn table(&self) -> &LazyTable {
self.request.table()
}
#[must_use]
pub const fn target(&self) -> &MssqlOutputTarget {
self.request.target()
}
#[must_use]
pub const fn resolved_target(&self) -> &ResolvedMssqlTarget {
&self.resolved_target
}
#[must_use]
pub const fn output_plan(&self) -> &MssqlTargetOutputPlan {
&self.output_plan
}
#[must_use]
pub fn phase_timings(&self) -> &[PhaseTimingReport] {
&self.phase_timings
}
}
#[cfg(test)]
mod tests {
use crate::{
DeltaFunnelError, LoadMode, MssqlConnectionConfig, MssqlTargetConfig, MssqlTargetTable,
};
use super::{LazyTable, LazyTableKind, MssqlOutputTarget, OutputWritePlan, RunMode};
#[test]
fn output_request_shapes_preserve_table_target_and_run_mode() -> Result<(), DeltaFunnelError> {
let table = LazyTable::placeholder(7, LazyTableKind::DerivedSql);
let target_config = MssqlTargetConfig::new(MssqlTargetTable::new("dbo", "orders")?)
.with_load_mode(LoadMode::CreateAndLoad)
.with_connection(
MssqlConnectionConfig::new(
"server=tcp:sql.example.com;database=warehouse;user=admin;password=secret-token",
)?
.with_display_label("warehouse-primary"),
);
let target = MssqlOutputTarget::new("orders_output", target_config, RunMode::DryRun);
let plan = OutputWritePlan::new(table.clone(), target.clone());
assert_eq!(table.id(), 7);
assert_eq!(table.kind(), LazyTableKind::DerivedSql);
assert_eq!(target.output_name(), "orders_output");
assert_eq!(target.run_mode(), RunMode::DryRun);
assert_eq!(target.target().load_mode(), LoadMode::CreateAndLoad);
assert_eq!(plan.table(), &table);
assert_eq!(plan.target(), &target);
let debug = format!("{target:?}");
assert!(debug.contains("orders_output"));
assert!(!debug.contains("secret-token"));
assert!(!debug.contains("password"));
assert!(!debug.contains("server=tcp"));
Ok(())
}
}