use facet::Facet;
use facet_pretty::PrettyPrinter;
use facet_testhelpers::test;
use insta::assert_snapshot;
#[derive(Facet, Clone, Debug)]
#[facet(transparent)]
pub struct IntAsString(pub String);
#[derive(Facet, Debug, Clone, PartialEq)]
#[facet(proxy = IntAsString)]
pub struct ProxyInt {
pub value: i32,
}
#[derive(Facet, Debug, Clone, PartialEq)]
pub struct ProxyIntAliased {
pub a: std::rc::Rc<ProxyInt>,
pub b: std::rc::Rc<ProxyInt>,
}
impl TryFrom<IntAsString> for ProxyInt {
type Error = std::num::ParseIntError;
fn try_from(proxy: IntAsString) -> Result<Self, Self::Error> {
Ok(ProxyInt {
value: proxy.0.parse()?,
})
}
}
impl From<&ProxyInt> for IntAsString {
fn from(v: &ProxyInt) -> Self {
IntAsString(v.value.to_string())
}
}
#[test]
fn test_proxy_container_pretty_print() {
let proxy_int = ProxyInt { value: 42 };
let output = PrettyPrinter::new()
.with_colors(false.into())
.format(&proxy_int);
assert_snapshot!(output, @r#""42""#);
}
#[derive(Facet, Debug, Clone, PartialEq)]
pub struct ProxyFieldLevel {
pub name: String,
#[facet(proxy = IntAsString)]
pub count: i32,
}
impl TryFrom<IntAsString> for i32 {
type Error = std::num::ParseIntError;
fn try_from(proxy: IntAsString) -> Result<Self, Self::Error> {
proxy.0.parse()
}
}
impl From<&i32> for IntAsString {
fn from(v: &i32) -> Self {
IntAsString(v.to_string())
}
}
#[test]
fn test_proxy_field_level_pretty_print() {
let data = ProxyFieldLevel {
name: "test".to_string(),
count: 100,
};
let output = PrettyPrinter::new().with_colors(false.into()).format(&data);
assert_snapshot!(output, @r#"
ProxyFieldLevel {
name: "test",
count: "100",
}
"#);
}
#[test]
fn test_proxy_container_format_peek_with_spans() {
use facet_reflect::Peek;
let proxy_int = ProxyInt { value: 42 };
let formatted = PrettyPrinter::new().format_peek_with_spans(Peek::new(&proxy_int));
assert_snapshot!(formatted.text, @r#""42""#);
assert!(!formatted.spans.is_empty());
}
#[test]
fn test_proxy_container_aliasing_format_peek_with_spans() {
use facet_reflect::Peek;
use std::rc::Rc;
let shared = Rc::new(ProxyInt { value: 42 });
let value = ProxyIntAliased {
a: shared.clone(),
b: shared,
};
let formatted = PrettyPrinter::new().format_peek_with_spans(Peek::new(&value));
assert!(
!formatted.text.contains("cycle detected"),
"aliasing a proxied value should not be treated as a cycle"
);
assert_snapshot!(formatted.text, @r#"
ProxyIntAliased {
a: "42",
b: "42",
}
"#);
}
#[test]
fn test_proxy_inside_arc_aliased_struct() {
use facet_reflect::Peek;
use std::sync::Arc;
#[derive(Facet, Debug, Clone)]
pub struct InnerWithProxy {
pub name: String,
#[facet(proxy = IntAsString)]
pub count: i32,
}
#[derive(Facet, Debug, Clone)]
pub struct OuterWithArcAliasing {
pub direct: Arc<InnerWithProxy>,
pub nested: NestedArcRef,
}
#[derive(Facet, Debug, Clone)]
pub struct NestedArcRef {
pub inner: Arc<InnerWithProxy>,
}
let shared = Arc::new(InnerWithProxy {
name: "test".to_string(),
count: 42,
});
let value = OuterWithArcAliasing {
direct: shared.clone(),
nested: NestedArcRef { inner: shared },
};
let formatted = PrettyPrinter::new()
.with_colors(false.into())
.format_peek_with_spans(Peek::new(&value));
assert!(
!formatted.text.contains("cycle detected"),
"proxy fields inside Arc-aliased structs should not trigger false cycle detection. Got:\n{}",
formatted.text
);
assert!(
formatted.text.matches("\"42\"").count() == 2,
"expected 2 occurrences of the proxied value \"42\", got:\n{}",
formatted.text
);
}
#[test]
fn test_proxy_field_level_format_peek_with_spans() {
use facet_pretty::PathSegment;
use facet_reflect::Peek;
use std::borrow::Cow;
let data = ProxyFieldLevel {
name: "test".to_string(),
count: 100,
};
let formatted = PrettyPrinter::new().format_peek_with_spans(Peek::new(&data));
assert_snapshot!(formatted.text, @r#"
ProxyFieldLevel {
name: "test",
count: "100",
}
"#);
let count_path = vec![PathSegment::Field(Cow::Borrowed("count"))];
assert!(
formatted.spans.contains_key(&count_path),
"count field span not found"
);
if let Some(span) = formatted.spans.get(&count_path) {
let value_text = &formatted.text[span.value.0..span.value.1];
assert_eq!(value_text, "\"100\"");
}
}