braze_sync/format/mod.rs
1//! Output formatters for diff results.
2//!
3//! Two formatters are exposed:
4//!
5//! - [`TableFormatter`] — human-readable, multi-resource indented layout
6//! matching IMPLEMENTATION.md §7.4. v0.1.0 ships without ANSI colors;
7//! the global `--no-color` flag is therefore a no-op until a future
8//! cosmetic pass adds color.
9//! - [`JsonFormatter`] — frozen v1 schema for CI consumption (§12). The
10//! wire shape carries an explicit `version: 1` field so consumers can
11//! branch on a future schema bump.
12//!
13//! The wire types in [`json`] are deliberately separate from the domain
14//! types in [`crate::resource`] / [`crate::diff`]. Refactoring a domain
15//! type cannot accidentally change the public JSON contract.
16
17pub mod json;
18pub mod table;
19
20use crate::diff::DiffSummary;
21use clap::ValueEnum;
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum, Default)]
24#[value(rename_all = "snake_case")]
25pub enum OutputFormat {
26 #[default]
27 Table,
28 Json,
29}
30
31/// Format a [`DiffSummary`] for display. Implementations are stateless
32/// unit structs.
33pub trait DiffFormatter {
34 fn format(&self, summary: &DiffSummary) -> String;
35}
36
37#[derive(Debug, Default, Clone, Copy)]
38pub struct TableFormatter;
39
40#[derive(Debug, Default, Clone, Copy)]
41pub struct JsonFormatter;
42
43impl DiffFormatter for TableFormatter {
44 fn format(&self, summary: &DiffSummary) -> String {
45 table::render(summary)
46 }
47}
48
49impl DiffFormatter for JsonFormatter {
50 fn format(&self, summary: &DiffSummary) -> String {
51 json::render(summary)
52 }
53}
54
55impl OutputFormat {
56 /// Pick the formatter implementation for this format.
57 pub fn formatter(self) -> Box<dyn DiffFormatter> {
58 match self {
59 Self::Table => Box::new(TableFormatter),
60 Self::Json => Box::new(JsonFormatter),
61 }
62 }
63}
64
65#[cfg(test)]
66mod fixtures;
67
68#[cfg(test)]
69mod snapshot_tests;