use facet::Facet;
use facet_diff::FacetDiff;
use facet_diff_core::{
BuildOptions, JsonFlavor, RenderOptions, RustFlavor, XmlFlavor, build_layout, render_to_string,
};
use facet_reflect::Peek;
use facet_value::{VObject, Value};
use facet_xml as xml;
fn print_all_flavors<'a, T: facet::Facet<'a>>(label: &str, from: &T, to: &T) {
let opts = RenderOptions::default();
let build_opts = BuildOptions::default();
println!("=== {} ===\n", label);
let diff = from.diff(to);
println!("--- Rust ---");
let layout = build_layout(
&diff,
Peek::new(from),
Peek::new(to),
&build_opts,
&RustFlavor,
);
println!("{}", render_to_string(&layout, &opts, &RustFlavor));
println!("--- JSON ---");
let layout = build_layout(
&diff,
Peek::new(from),
Peek::new(to),
&build_opts,
&JsonFlavor,
);
println!("{}", render_to_string(&layout, &opts, &JsonFlavor));
println!("--- XML ---");
let layout = build_layout(
&diff,
Peek::new(from),
Peek::new(to),
&build_opts,
&XmlFlavor,
);
println!("{}", render_to_string(&layout, &opts, &XmlFlavor));
println!();
}
fn main() {
{
#[derive(Facet, Debug)]
struct Point {
x: i32,
y: i32,
}
let from = Point { x: 10, y: 20 };
let to = Point { x: 30, y: 20 };
print_all_flavors("Struct diff (one field changed)", &from, &to);
}
{
#[derive(Facet, Debug)]
struct Point {
x: i32,
y: i32,
}
let from = Point { x: 10, y: 20 };
let to = Point { x: 3235832, y: 2 };
print_all_flavors("Struct diff (all fields changed)", &from, &to);
}
{
#[derive(Facet, Debug)]
struct Outer {
name: &'static str,
point: Inner,
}
#[derive(Facet, Debug)]
struct Inner {
x: i32,
y: i32,
}
let from = Outer {
name: "origin",
point: Inner { x: 0, y: 0 },
};
let to = Outer {
name: "origin",
point: Inner { x: 10, y: 0 },
};
print_all_flavors("Nested struct diff", &from, &to);
}
{
let from = vec![1, 2, 3, 4, 5];
let to = vec![1, 2, 99, 4, 5];
print_all_flavors("Sequence diff", &from, &to);
}
{
let from: Vec<i32> = (0..20).collect();
let mut to = from.clone();
to[10] = 999;
print_all_flavors("Sequence diff (with collapsing)", &from, &to);
}
{
let from: Vec<i32> = (100..150).collect(); let mut to = from.clone();
to[25] = 9999;
print_all_flavors("Long sequence (XML width test)", &from, &to);
}
{
let mut from_obj = VObject::new();
from_obj.insert("name", "server");
from_obj.insert("port", 8080);
let from: Value = from_obj.into();
let mut to_obj = VObject::new();
to_obj.insert("name", "server");
to_obj.insert("port", 8080);
to_obj.insert("host", "localhost");
to_obj.insert("timeout", 30);
to_obj.insert("debug", true);
let to: Value = to_obj.into();
print_all_flavors("Field additions (multiple)", &from, &to);
}
{
let mut from_obj = VObject::new();
from_obj.insert("name", "server");
from_obj.insert("port", 8080);
from_obj.insert("host", "localhost");
from_obj.insert("timeout", 30);
from_obj.insert("debug", true);
let from: Value = from_obj.into();
let mut to_obj = VObject::new();
to_obj.insert("name", "server");
to_obj.insert("port", 8080);
let to: Value = to_obj.into();
print_all_flavors("Field removals (multiple)", &from, &to);
}
{
let mut from_obj = VObject::new();
from_obj.insert("name", "old-server");
from_obj.insert("port", 80);
from_obj.insert("legacy", true);
let from: Value = from_obj.into();
let mut to_obj = VObject::new();
to_obj.insert("name", "new-server");
to_obj.insert("port", 443);
to_obj.insert("secure", true);
to_obj.insert("tls_version", "1.3");
let to: Value = to_obj.into();
print_all_flavors("Mixed changes (add, remove, modify)", &from, &to);
}
{
#[derive(Facet, Debug)]
#[facet(rename = "svg")]
struct Svg {
#[facet(xml::attribute)]
width: u32,
#[facet(xml::attribute)]
height: u32,
#[facet(xml::element, rename = "viewBox")]
viewbox: ViewBox,
#[facet(xml::elements)]
groups: Vec<Group>,
}
#[derive(Facet, Debug)]
#[facet(rename = "viewBox")]
struct ViewBox {
#[facet(xml::attribute, rename = "minX")]
min_x: i32,
#[facet(xml::attribute, rename = "minY")]
min_y: i32,
#[facet(xml::attribute)]
width: u32,
#[facet(xml::attribute)]
height: u32,
}
#[derive(Facet, Debug)]
#[facet(rename = "g")]
struct Group {
#[facet(xml::attribute)]
id: &'static str,
#[facet(xml::element)]
transform: Transform,
#[facet(xml::element)]
style: Style,
}
#[derive(Facet, Debug)]
#[facet(rename = "transform", rename_all = "kebab-case")]
struct Transform {
#[facet(xml::attribute)]
translate_x: f32,
#[facet(xml::attribute)]
translate_y: f32,
#[facet(xml::attribute)]
scale: f32,
#[facet(xml::attribute)]
rotate: f32,
}
#[derive(Facet, Debug)]
#[facet(rename = "style", rename_all = "kebab-case")]
struct Style {
#[facet(xml::attribute)]
fill: &'static str,
#[facet(xml::attribute)]
stroke: &'static str,
#[facet(xml::attribute)]
stroke_width: f32,
#[facet(xml::attribute)]
opacity: f32,
}
let from = Svg {
width: 800,
height: 600,
viewbox: ViewBox {
min_x: 0,
min_y: 0,
width: 800,
height: 600,
},
groups: vec![
Group {
id: "rect-group",
transform: Transform {
translate_x: 100.0,
translate_y: 100.0,
scale: 1.0,
rotate: 0.0,
},
style: Style {
fill: "blue",
stroke: "black",
stroke_width: 2.0,
opacity: 1.0,
},
},
Group {
id: "circle-group",
transform: Transform {
translate_x: 400.0,
translate_y: 300.0,
scale: 1.0,
rotate: 0.0,
},
style: Style {
fill: "red",
stroke: "none",
stroke_width: 0.0,
opacity: 0.8,
},
},
],
};
let to = Svg {
width: 1024, height: 768, viewbox: ViewBox {
min_x: 0,
min_y: 0,
width: 1024, height: 768, },
groups: vec![
Group {
id: "rect-group",
transform: Transform {
translate_x: 150.0, translate_y: 120.0, scale: 1.5, rotate: 45.0, },
style: Style {
fill: "green", stroke: "white", stroke_width: 3.0, opacity: 0.9, },
},
Group {
id: "circle-group",
transform: Transform {
translate_x: 500.0, translate_y: 400.0, scale: 2.0, rotate: 0.0,
},
style: Style {
fill: "yellow", stroke: "orange", stroke_width: 1.0, opacity: 1.0, },
},
],
};
print_all_flavors("Deep nesting (SVG-like)", &from, &to);
}
{
#[derive(Facet, Debug)]
struct ManyAttrs {
name: &'static str,
id: u32,
enabled: bool,
visible: bool,
priority: i32,
weight: f32,
category: &'static str,
tags: &'static str,
version: u32,
revision: u32,
status: &'static str,
owner: &'static str,
}
let from = ManyAttrs {
name: "widget",
id: 1001,
enabled: true,
visible: true,
priority: 5,
weight: 1.0,
category: "ui",
tags: "interactive,clickable",
version: 1,
revision: 0,
status: "active",
owner: "alice",
};
let to = ManyAttrs {
name: "super-widget", id: 1001,
enabled: false, visible: true,
priority: 10, weight: 2.5, category: "ui",
tags: "interactive,draggable,resizable", version: 2, revision: 3, status: "updated", owner: "bob", };
print_all_flavors("Many attributes (12 fields)", &from, &to);
}
{
#[derive(Facet, Debug)]
struct Metadata {
created_by: &'static str,
version: u32,
}
#[derive(Facet, Debug)]
struct Bounds {
min: i32,
max: i32,
}
#[derive(Facet, Debug)]
struct Config {
name: &'static str,
enabled: bool,
metadata: Metadata,
bounds: Bounds,
tags: &'static str,
}
let from = Config {
name: "widget",
enabled: true,
metadata: Metadata {
created_by: "alice",
version: 1,
},
bounds: Bounds { min: 0, max: 100 },
tags: "ui,interactive",
};
let to = Config {
name: "super-widget",
enabled: false,
metadata: Metadata {
created_by: "bob",
version: 2,
},
bounds: Bounds { min: -10, max: 200 },
tags: "ui,draggable",
};
print_all_flavors("Mixed scalar and struct fields", &from, &to);
}
{
#[derive(Facet, Debug)]
struct Circle {
radius: f32,
center_x: f32,
center_y: f32,
}
#[derive(Facet, Debug)]
struct Rectangle {
width: f32,
height: f32,
x: f32,
y: f32,
}
#[derive(Facet, Debug)]
#[repr(u8)]
enum Shape {
#[allow(dead_code)]
Circle(Circle),
#[allow(dead_code)]
Rectangle(Rectangle),
}
#[derive(Facet, Debug)]
struct Drawing {
name: &'static str,
shape: Shape,
}
let from = Drawing {
name: "my-shape",
shape: Shape::Circle(Circle {
radius: 50.0,
center_x: 100.0,
center_y: 100.0,
}),
};
let to = Drawing {
name: "my-shape",
shape: Shape::Rectangle(Rectangle {
width: 80.0,
height: 60.0,
x: 50.0,
y: 70.0,
}),
};
print_all_flavors("Enum variant change", &from, &to);
}
}