1use std::fmt::Write;
26
27use facet_diff_core::{
28 AnsiBackend, BuildOptions, Diff, PlainBackend, RenderOptions, XmlFlavor, build_layout, render,
29 render_to_string,
30};
31pub use facet_diff_core::{DiffSymbols, DiffTheme};
32use facet_reflect::Peek;
33
34#[derive(Debug, Clone)]
36pub struct DiffSerializeOptions {
37 pub symbols: DiffSymbols,
39
40 pub theme: DiffTheme,
42
43 pub colors: bool,
45
46 pub indent: &'static str,
48
49 pub max_line_width: usize,
51
52 pub max_unchanged_fields: usize,
54
55 pub collapse_threshold: usize,
57}
58
59impl Default for DiffSerializeOptions {
60 fn default() -> Self {
61 Self {
62 symbols: DiffSymbols::default(),
63 theme: DiffTheme::default(),
64 colors: true,
65 indent: " ",
66 max_line_width: 80,
67 max_unchanged_fields: 5,
68 collapse_threshold: 3,
69 }
70 }
71}
72
73impl DiffSerializeOptions {
74 pub fn new() -> Self {
76 Self::default()
77 }
78
79 pub const fn no_colors(mut self) -> Self {
81 self.colors = false;
82 self
83 }
84
85 pub const fn indent(mut self, indent: &'static str) -> Self {
87 self.indent = indent;
88 self
89 }
90
91 pub const fn max_line_width(mut self, width: usize) -> Self {
93 self.max_line_width = width;
94 self
95 }
96
97 pub const fn max_unchanged_fields(mut self, count: usize) -> Self {
99 self.max_unchanged_fields = count;
100 self
101 }
102
103 pub const fn collapse_threshold(mut self, threshold: usize) -> Self {
105 self.collapse_threshold = threshold;
106 self
107 }
108}
109
110pub fn diff_to_string<'mem, 'facet>(
131 from: &'mem impl facet_core::Facet<'facet>,
132 to: &'mem impl facet_core::Facet<'facet>,
133 diff: &Diff<'mem, 'facet>,
134) -> String {
135 diff_to_string_with_options(from, to, diff, &DiffSerializeOptions::default())
136}
137
138pub fn diff_to_string_with_options<'mem, 'facet>(
147 from: &'mem impl facet_core::Facet<'facet>,
148 to: &'mem impl facet_core::Facet<'facet>,
149 diff: &Diff<'mem, 'facet>,
150 options: &DiffSerializeOptions,
151) -> String {
152 let from_peek = Peek::new(from);
153 let to_peek = Peek::new(to);
154
155 let build_opts = BuildOptions {
156 max_line_width: options.max_line_width,
157 max_unchanged_fields: options.max_unchanged_fields,
158 collapse_threshold: options.collapse_threshold,
159 float_precision: None,
160 };
161
162 let flavor = XmlFlavor;
163 let layout = build_layout(diff, from_peek, to_peek, &build_opts, &flavor);
164
165 if options.colors {
166 let render_opts = RenderOptions {
167 indent: options.indent,
168 symbols: options.symbols.clone(),
169 backend: AnsiBackend::new(options.theme.clone()),
170 };
171 render_to_string(&layout, &render_opts, &flavor)
172 } else {
173 let render_opts = RenderOptions {
174 indent: options.indent,
175 symbols: options.symbols.clone(),
176 backend: PlainBackend,
177 };
178 render_to_string(&layout, &render_opts, &flavor)
179 }
180}
181
182pub fn diff_to_writer<'mem, 'facet, W: Write>(
191 from: &'mem impl facet_core::Facet<'facet>,
192 to: &'mem impl facet_core::Facet<'facet>,
193 diff: &Diff<'mem, 'facet>,
194 writer: &mut W,
195) -> std::fmt::Result {
196 diff_to_writer_with_options(from, to, diff, writer, &DiffSerializeOptions::default())
197}
198
199pub fn diff_to_writer_with_options<'mem, 'facet, W: Write>(
209 from: &'mem impl facet_core::Facet<'facet>,
210 to: &'mem impl facet_core::Facet<'facet>,
211 diff: &Diff<'mem, 'facet>,
212 writer: &mut W,
213 options: &DiffSerializeOptions,
214) -> std::fmt::Result {
215 let from_peek = Peek::new(from);
216 let to_peek = Peek::new(to);
217
218 let build_opts = BuildOptions {
219 max_line_width: options.max_line_width,
220 max_unchanged_fields: options.max_unchanged_fields,
221 collapse_threshold: options.collapse_threshold,
222 float_precision: None,
223 };
224
225 let flavor = XmlFlavor;
226 let layout = build_layout(diff, from_peek, to_peek, &build_opts, &flavor);
227
228 if options.colors {
229 let render_opts = RenderOptions {
230 indent: options.indent,
231 symbols: options.symbols.clone(),
232 backend: AnsiBackend::new(options.theme.clone()),
233 };
234 render(&layout, writer, &render_opts, &flavor)
235 } else {
236 let render_opts = RenderOptions {
237 indent: options.indent,
238 symbols: options.symbols.clone(),
239 backend: PlainBackend,
240 };
241 render(&layout, writer, &render_opts, &flavor)
242 }
243}