rust_pgdatadiff/diff/table/query/output/
mod.rs

1use colored::{ColoredString, Colorize};
2use std::fmt::Display;
3
4use crate::diff::diff_output::DiffOutput;
5use crate::diff::types::DiffOutputMarker;
6use std::time::Duration;
7
8/// Represents the source of a table (either the first or the second).
9#[cfg_attr(test, derive(PartialEq))]
10#[derive(Debug, Clone)]
11pub enum TableSource {
12    First,
13    Second,
14}
15
16impl Display for TableSource {
17    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18        match self {
19            Self::First => write!(f, "first"),
20            Self::Second => write!(f, "second"),
21        }
22    }
23}
24
25/// Represents the difference in table counts between two tables.
26#[cfg_attr(test, derive(PartialEq))]
27#[derive(Debug, Clone)]
28pub struct TableCountDiff(i64, i64);
29
30impl TableCountDiff {
31    /// Creates a new `TableCountDiff` instance with the given counts.
32    pub fn new(first: i64, second: i64) -> Self {
33        Self(first, second)
34    }
35
36    pub fn first(&self) -> i64 {
37        self.0
38    }
39
40    pub fn second(&self) -> i64 {
41        self.1
42    }
43}
44
45/// Represents the output of a table difference.
46#[cfg_attr(test, derive(PartialEq))]
47#[derive(Debug, Clone)]
48pub enum TableDiffOutput {
49    /// Indicates that there is no difference between the tables.
50    NoCountDiff(String, i64),
51    /// Indicates that there is no difference between the tables, along with the duration of the comparison.
52    NoDiffWithDuration(String, Duration),
53    /// Indicates that the table does not exist in a specific source.
54    NotExists(String, TableSource),
55    /// Indicates a difference in table counts.
56    Diff(String, TableCountDiff),
57    /// Indicates that no primary key was found in the table.
58    NoPrimaryKeyFound(String),
59    /// Indicates a difference in table data, along with the duration of the comparison.
60    DataDiffWithDuration(String, i64, i64, Duration),
61}
62
63impl TableDiffOutput {
64    /// Determines whether the table difference should be skipped.
65    pub fn skip_table_diff(&self) -> bool {
66        matches!(self, Self::Diff(_, _) | Self::NotExists(_, _))
67    }
68
69    /// Converts the table difference output to a colored string.
70    pub fn to_string(&self) -> ColoredString {
71        match self {
72            Self::NoCountDiff(table, count) => {
73                format!("{} - No difference. Total rows: {}", table, count)
74                    .green()
75                    .bold()
76            }
77            Self::NotExists(table, source) => format!("{} - Does not exist in {}", table, source)
78                .red()
79                .bold()
80                .underline(),
81            Self::Diff(table, diffs) => format!(
82                "{} - First table rows: {}, Second table rows: {}",
83                table,
84                diffs.first(),
85                diffs.second()
86            )
87            .red()
88            .bold(),
89            TableDiffOutput::NoPrimaryKeyFound(table) => {
90                format!("{} - No primary key found", table).red().bold()
91            }
92            TableDiffOutput::NoDiffWithDuration(table, duration) => {
93                format!("{} - No difference in {}ms", table, duration.as_millis())
94                    .green()
95                    .bold()
96            }
97            TableDiffOutput::DataDiffWithDuration(table_name, position, offset, duration) => {
98                format!(
99                    "{} - Data diff between rows [{},{}] - in {}ms",
100                    table_name,
101                    position,
102                    offset,
103                    duration.as_millis()
104                )
105                .red()
106                .bold()
107            }
108        }
109    }
110}
111
112impl DiffOutputMarker for TableDiffOutput {
113    fn convert(self) -> DiffOutput {
114        DiffOutput::TableDiff(self.clone())
115    }
116}
117
118impl From<TableDiffOutput> for DiffOutput {
119    fn from(val: TableDiffOutput) -> Self {
120        DiffOutput::TableDiff(val)
121    }
122}
123
124#[cfg(test)]
125mod tests {
126    use super::*;
127
128    #[test]
129    fn test_skip_table_when_needed() {
130        let no_count_diff = TableDiffOutput::NoCountDiff("test".to_string(), 1000);
131        let not_exists = TableDiffOutput::NotExists("test".to_string(), TableSource::First);
132        let diff = TableDiffOutput::Diff("test".to_string(), TableCountDiff::new(1, 2));
133        let no_primary_key = TableDiffOutput::NoPrimaryKeyFound("test".to_string());
134        let no_diff_with_duration =
135            TableDiffOutput::NoDiffWithDuration("test".to_string(), Duration::from_millis(1));
136        let data_diff_with_duration = TableDiffOutput::DataDiffWithDuration(
137            "test".to_string(),
138            1,
139            2,
140            Duration::from_millis(1),
141        );
142
143        assert!(not_exists.skip_table_diff());
144        assert!(diff.skip_table_diff());
145        assert!(!no_count_diff.skip_table_diff());
146        assert!(!no_primary_key.skip_table_diff());
147        assert!(!no_diff_with_duration.skip_table_diff());
148        assert!(!data_diff_with_duration.skip_table_diff());
149    }
150}