package diff import ( "encoding/json" "fmt" "io" "strings" "text/template" ) // OutputFormat represents the output format for diff results type OutputFormat string const ( FormatSummary OutputFormat = "summary" FormatJSON OutputFormat = "json" FormatHTML OutputFormat = "html" ) // FormatDiff formats the diff result according to the specified format func FormatDiff(result *DiffResult, format OutputFormat, w io.Writer) error { switch format { case FormatSummary: return formatSummary(result, w) case FormatJSON: return formatJSON(result, w) case FormatHTML: return formatHTML(result, w) default: return fmt.Errorf("unsupported format: %s", format) } } func formatSummary(result *DiffResult, w io.Writer) error { summary := ComputeSummary(result) fmt.Fprintf(w, "\n=== Database Diff Summary ===\n") fmt.Fprintf(w, "Source: %s\n", result.Source) fmt.Fprintf(w, "Target: %s\n\n", result.Target) // Schemas if summary.Schemas.Missing > 0 || summary.Schemas.Extra > 0 || summary.Schemas.Modified > 0 { fmt.Fprintf(w, "Schemas:\n") if summary.Schemas.Missing > 0 { fmt.Fprintf(w, " Missing: %d\n", summary.Schemas.Missing) } if summary.Schemas.Extra > 0 { fmt.Fprintf(w, " Extra: %d\n", summary.Schemas.Extra) } if summary.Schemas.Modified > 0 { fmt.Fprintf(w, " Modified: %d\n", summary.Schemas.Modified) } fmt.Fprintf(w, "\n") } // Tables if summary.Tables.Missing > 0 || summary.Tables.Extra > 0 || summary.Tables.Modified > 0 { fmt.Fprintf(w, "Tables:\n") if summary.Tables.Missing > 0 { fmt.Fprintf(w, " Missing: %d\n", summary.Tables.Missing) } if summary.Tables.Extra > 0 { fmt.Fprintf(w, " Extra: %d\n", summary.Tables.Extra) } if summary.Tables.Modified > 0 { fmt.Fprintf(w, " Modified: %d\n", summary.Tables.Modified) } fmt.Fprintf(w, "\n") } // Columns if summary.Columns.Missing > 0 || summary.Columns.Extra > 0 || summary.Columns.Modified > 0 { fmt.Fprintf(w, "Columns:\n") if summary.Columns.Missing > 0 { fmt.Fprintf(w, " Missing: %d\n", summary.Columns.Missing) } if summary.Columns.Extra > 0 { fmt.Fprintf(w, " Extra: %d\n", summary.Columns.Extra) } if summary.Columns.Modified > 0 { fmt.Fprintf(w, " Modified: %d\n", summary.Columns.Modified) } fmt.Fprintf(w, "\n") } // Indexes if summary.Indexes.Missing > 0 || summary.Indexes.Extra > 0 || summary.Indexes.Modified > 0 { fmt.Fprintf(w, "Indexes:\n") if summary.Indexes.Missing > 0 { fmt.Fprintf(w, " Missing: %d\n", summary.Indexes.Missing) } if summary.Indexes.Extra > 0 { fmt.Fprintf(w, " Extra: %d\n", summary.Indexes.Extra) } if summary.Indexes.Modified > 0 { fmt.Fprintf(w, " Modified: %d\n", summary.Indexes.Modified) } fmt.Fprintf(w, "\n") } // Constraints if summary.Constraints.Missing > 0 || summary.Constraints.Extra > 0 || summary.Constraints.Modified > 0 { fmt.Fprintf(w, "Constraints:\n") if summary.Constraints.Missing > 0 { fmt.Fprintf(w, " Missing: %d\n", summary.Constraints.Missing) } if summary.Constraints.Extra > 0 { fmt.Fprintf(w, " Extra: %d\n", summary.Constraints.Extra) } if summary.Constraints.Modified > 0 { fmt.Fprintf(w, " Modified: %d\n", summary.Constraints.Modified) } fmt.Fprintf(w, "\n") } // Relationships if summary.Relationships.Missing > 0 || summary.Relationships.Extra > 0 || summary.Relationships.Modified > 0 { fmt.Fprintf(w, "Relationships:\n") if summary.Relationships.Missing > 0 { fmt.Fprintf(w, " Missing: %d\n", summary.Relationships.Missing) } if summary.Relationships.Extra > 0 { fmt.Fprintf(w, " Extra: %d\n", summary.Relationships.Extra) } if summary.Relationships.Modified > 0 { fmt.Fprintf(w, " Modified: %d\n", summary.Relationships.Modified) } fmt.Fprintf(w, "\n") } // Views if summary.Views.Missing > 0 || summary.Views.Extra > 0 || summary.Views.Modified > 0 { fmt.Fprintf(w, "Views:\n") if summary.Views.Missing > 0 { fmt.Fprintf(w, " Missing: %d\n", summary.Views.Missing) } if summary.Views.Extra > 0 { fmt.Fprintf(w, " Extra: %d\n", summary.Views.Extra) } if summary.Views.Modified > 0 { fmt.Fprintf(w, " Modified: %d\n", summary.Views.Modified) } fmt.Fprintf(w, "\n") } // Sequences if summary.Sequences.Missing > 0 || summary.Sequences.Extra > 0 || summary.Sequences.Modified > 0 { fmt.Fprintf(w, "Sequences:\n") if summary.Sequences.Missing > 0 { fmt.Fprintf(w, " Missing: %d\n", summary.Sequences.Missing) } if summary.Sequences.Extra > 0 { fmt.Fprintf(w, " Extra: %d\n", summary.Sequences.Extra) } if summary.Sequences.Modified > 0 { fmt.Fprintf(w, " Modified: %d\n", summary.Sequences.Modified) } fmt.Fprintf(w, "\n") } // Check if there are no differences if summary.Schemas.Missing == 0 && summary.Schemas.Extra == 0 && summary.Schemas.Modified == 0 && summary.Tables.Missing == 0 && summary.Tables.Extra == 0 && summary.Tables.Modified == 0 && summary.Columns.Missing == 0 && summary.Columns.Extra == 0 && summary.Columns.Modified == 0 && summary.Indexes.Missing == 0 && summary.Indexes.Extra == 0 && summary.Indexes.Modified == 0 && summary.Constraints.Missing == 0 && summary.Constraints.Extra == 0 && summary.Constraints.Modified == 0 && summary.Relationships.Missing == 0 && summary.Relationships.Extra == 0 && summary.Relationships.Modified == 0 && summary.Views.Missing == 0 && summary.Views.Extra == 0 && summary.Views.Modified == 0 && summary.Sequences.Missing == 0 && summary.Sequences.Extra == 0 && summary.Sequences.Modified == 0 { fmt.Fprintf(w, "No differences found.\n") } return nil } func formatJSON(result *DiffResult, w io.Writer) error { encoder := json.NewEncoder(w) encoder.SetIndent("", " ") return encoder.Encode(result) } func formatHTML(result *DiffResult, w io.Writer) error { tmpl, err := template.New("diff").Funcs(template.FuncMap{ "join": strings.Join, }).Parse(htmlTemplate) if err != nil { return fmt.Errorf("failed to parse template: %w", err) } summary := ComputeSummary(result) data := struct { Result *DiffResult Summary *Summary }{ Result: result, Summary: summary, } return tmpl.Execute(w, data) } const htmlTemplate = `
Source: {{.Result.Source}}
Target: {{.Result.Target}}