use schemars::JsonSchema;
use serde::Serialize;
#[derive(Debug, Clone, PartialEq, Serialize, JsonSchema)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ConvertWarning {
UnsupportedFeature {
feature: String,
suggestion: Option<String>,
},
LossyConversion {
from_type: String,
to_type: String,
table: Option<String>,
column: Option<String>,
},
SkippedStatement {
reason: String,
statement_preview: String,
},
CopyNotConverted { table: String },
}
impl std::fmt::Display for ConvertWarning {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ConvertWarning::UnsupportedFeature {
feature,
suggestion,
} => {
write!(f, "Unsupported feature: {}", feature)?;
if let Some(s) = suggestion {
write!(f, " ({})", s)?;
}
Ok(())
}
ConvertWarning::LossyConversion {
from_type,
to_type,
table,
column,
} => {
write!(f, "Lossy conversion: {} → {}", from_type, to_type)?;
if let Some(t) = table {
write!(f, " in table {}", t)?;
if let Some(c) = column {
write!(f, ".{}", c)?;
}
}
Ok(())
}
ConvertWarning::SkippedStatement {
reason,
statement_preview,
} => {
write!(f, "Skipped: {} ({})", reason, statement_preview)
}
ConvertWarning::CopyNotConverted { table } => {
write!(
f,
"COPY statement for table '{}' not converted - requires INSERT conversion",
table
)
}
}
}
}
#[derive(Debug, Default)]
pub struct WarningCollector {
warnings: Vec<ConvertWarning>,
max_warnings: usize,
}
impl WarningCollector {
pub fn new() -> Self {
Self {
warnings: Vec::new(),
max_warnings: 100, }
}
pub fn with_limit(limit: usize) -> Self {
Self {
warnings: Vec::new(),
max_warnings: limit,
}
}
pub fn add(&mut self, warning: ConvertWarning) {
if self.warnings.len() < self.max_warnings {
if !self.warnings.iter().any(|w| Self::is_similar(w, &warning)) {
self.warnings.push(warning);
}
}
}
fn is_similar(a: &ConvertWarning, b: &ConvertWarning) -> bool {
match (a, b) {
(
ConvertWarning::UnsupportedFeature { feature: f1, .. },
ConvertWarning::UnsupportedFeature { feature: f2, .. },
) => f1 == f2,
(
ConvertWarning::LossyConversion {
from_type: f1,
to_type: t1,
..
},
ConvertWarning::LossyConversion {
from_type: f2,
to_type: t2,
..
},
) => f1 == f2 && t1 == t2,
_ => false,
}
}
pub fn warnings(&self) -> &[ConvertWarning] {
&self.warnings
}
pub fn has_warnings(&self) -> bool {
!self.warnings.is_empty()
}
pub fn count(&self) -> usize {
self.warnings.len()
}
pub fn print_summary(&self) {
print_warnings_summary(&self.warnings, self.max_warnings);
}
}
pub fn print_warnings_summary(warnings: &[ConvertWarning], max_warnings: usize) {
if warnings.is_empty() {
return;
}
eprintln!("\nConversion warnings ({}):", warnings.len());
for warning in warnings {
eprintln!(" ⚠ {}", warning);
}
if warnings.len() >= max_warnings {
eprintln!(" ... (additional warnings truncated)");
}
}