use std::alloc::Layout;
use std::fmt::{self, Debug, Write};
use std::rc::Rc;
use std::{cmp::Ordering, collections::BTreeSet};
use facet_core::{Facet, PtrConst, PtrMut, PtrUninit, Shape};
use facet_macros::Facet;
struct NoopWriter;
impl Write for NoopWriter {
fn write_str(&mut self, _s: &str) -> fmt::Result {
Ok(())
}
}
fn probe_debug(shape: &'static Shape, ptr: PtrConst) -> bool {
struct DebugProbe(&'static Shape, PtrConst);
impl Debug for DebugProbe {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match unsafe { self.0.call_debug(self.1, f) } {
Some(result) => result,
None => Err(fmt::Error),
}
}
}
let mut writer = NoopWriter;
write!(writer, "{:?}", DebugProbe(shape, ptr)).is_ok()
}
fn probe_display(shape: &'static Shape, ptr: PtrConst) -> bool {
struct DisplayProbe(&'static Shape, PtrConst);
impl fmt::Display for DisplayProbe {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match unsafe { self.0.call_display(self.1, f) } {
Some(result) => result,
None => Err(fmt::Error),
}
}
}
let mut writer = NoopWriter;
write!(writer, "{}", DisplayProbe(shape, ptr)).is_ok()
}
use facet_testhelpers::test;
use owo_colors::{OwoColorize, Style};
const REMARKABLE: Style = Style::new().blue();
struct BoxPtrUninit {
ptr: PtrUninit,
layout: Layout,
shape: &'static Shape,
}
impl BoxPtrUninit {
fn new_sized<'a, T: Facet<'a> + ?Sized>() -> Self {
let layout = T::SHAPE.layout.sized_layout().expect("T must be Sized");
let ptr = if layout.size() == 0 {
core::ptr::without_provenance_mut(layout.align())
} else {
unsafe { std::alloc::alloc(layout) }
};
if ptr.is_null() {
std::alloc::handle_alloc_error(layout)
}
let ptr = PtrUninit::new(ptr);
Self {
ptr,
layout,
shape: T::SHAPE,
}
}
const unsafe fn assume_init(self) -> BoxPtrMut {
let r = BoxPtrMut {
ptr: unsafe { self.ptr.assume_init() },
layout: self.layout,
shape: self.shape,
};
core::mem::forget(self);
r
}
}
impl Drop for BoxPtrUninit {
fn drop(&mut self) {
if self.layout.size() > 0 {
unsafe { std::alloc::dealloc(self.ptr.as_mut_byte_ptr(), self.layout) };
}
}
}
struct BoxPtrMut {
ptr: PtrMut,
layout: Layout,
shape: &'static Shape,
}
impl Drop for BoxPtrMut {
fn drop(&mut self) {
let _ = unsafe { self.shape.call_drop_in_place(self.ptr) };
if self.layout.size() > 0 {
unsafe { std::alloc::dealloc(self.ptr.as_mut_byte_ptr(), self.layout) };
}
}
}
struct VTableValueView<'a, T: ?Sized>(&'a T);
impl<'f, 'a, T: Facet<'f> + ?Sized> Display for VTableValueView<'a, T> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
let ptr = PtrConst::new(core::ptr::from_ref(self.0));
match unsafe { T::SHAPE.call_display(ptr, f) } {
Some(result) => result,
None => write!(f, "???"),
}
}
}
impl<'f, 'a, T: Facet<'f> + ?Sized> Debug for VTableValueView<'a, T> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
let ptr = PtrConst::new(core::ptr::from_ref(self.0));
match unsafe { T::SHAPE.call_debug(ptr, f) } {
Some(result) => result,
None => write!(f, "???"),
}
}
}
unsafe fn debug(shape: &'static Shape, ptr: PtrConst) -> impl Debug {
struct Debugger(&'static Shape, PtrConst);
impl Debug for Debugger {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match unsafe { self.0.call_debug(self.1, f) } {
Some(result) => result,
None => write!(f, "???"),
}
}
}
Debugger(shape, ptr)
}
const fn ord_str(ordering: Option<Ordering>) -> &'static str {
match ordering {
Some(Ordering::Less) => "<",
Some(Ordering::Equal) => "==",
Some(Ordering::Greater) => ">",
None => "??",
}
}
fn collect_facts<'f, 'a, T>(val1: &'a T, val2: &'a T) -> BTreeSet<Fact>
where
T: Facet<'f> + ?Sized,
{
let mut facts: BTreeSet<Fact> = BTreeSet::new();
let shape = T::SHAPE;
let ptr1 = PtrConst::new(core::ptr::from_ref(val1));
let ptr2 = PtrConst::new(core::ptr::from_ref(val2));
let has_debug = probe_debug(shape, ptr1);
let has_display = probe_display(shape, ptr1);
let has_partial_eq = unsafe { shape.call_partial_eq(ptr1, ptr2) }.is_some();
let has_ord = unsafe { shape.call_cmp(ptr1, ptr2) }.is_some();
let has_partial_ord = unsafe { shape.call_partial_cmp(ptr1, ptr2) }.is_some();
let has_default = shape
.type_ops
.map(|ops| ops.has_default_in_place())
.unwrap_or(false)
&& {
let ptr = BoxPtrUninit::new_sized::<T>();
let result = unsafe { shape.call_default_in_place(ptr.ptr) };
if result.is_some() {
let _ = unsafe { ptr.assume_init() };
true
} else {
false
}
};
let has_clone = shape
.type_ops
.map(|ops| ops.has_clone_into())
.unwrap_or(false)
&& {
let ptr = BoxPtrUninit::new_sized::<T>();
let result = unsafe { shape.call_clone_into(ptr1, ptr.ptr.assume_init()) };
if result.is_some() {
let _ = unsafe { ptr.assume_init() };
true
} else {
false
}
};
let traits = [
("Debug", has_debug),
("Display", has_display),
("Default", has_default),
("PartialEq", has_partial_eq),
("Ord", has_ord),
("PartialOrd", has_partial_ord),
("Clone", has_clone),
];
let trait_str = traits
.iter()
.filter_map(|(name, has_impl)| {
if *has_impl {
Some(name.to_string())
} else {
None
}
})
.collect::<Vec<_>>()
.join(" + ");
eprintln!("{} {}", trait_str, "======".yellow());
let l = VTableValueView(val1);
let r = VTableValueView(val2);
if has_display {
facts.insert(Fact::Display);
eprintln!(
"Display: {}",
format_args!("{} vs {}", l.style(REMARKABLE), r.style(REMARKABLE))
);
}
if has_debug {
facts.insert(Fact::Debug);
eprintln!(
"Debug: {}",
format_args!("{:?} vs {:?}", l.style(REMARKABLE), r.style(REMARKABLE))
);
}
if has_partial_eq {
let eq_result = unsafe { shape.call_partial_eq(ptr1, ptr2) }.unwrap();
facts.insert(Fact::PartialEqAnd { l_eq_r: eq_result });
let eq_str = format!(
"{:?} {} {:?}",
l.style(REMARKABLE),
if eq_result { "==" } else { "!=" }.yellow(),
r.style(REMARKABLE),
);
eprintln!("Equality: {eq_str}");
}
if has_ord {
let cmp_result = unsafe { shape.call_cmp(ptr1, ptr2) }.unwrap();
facts.insert(Fact::OrdAnd {
l_ord_r: cmp_result,
});
let cmp_str = format!(
"{:?} {} {:?}",
l.style(REMARKABLE),
ord_str(Some(cmp_result)).yellow(),
r.style(REMARKABLE),
);
eprintln!("PartialOrd: {cmp_str}");
}
if has_partial_ord {
let cmp_result = unsafe { shape.call_partial_cmp(ptr1, ptr2) }.unwrap();
facts.insert(Fact::PartialOrdAnd {
l_ord_r: cmp_result,
});
let cmp_str = format!(
"{:?} {} {:?}",
l.style(REMARKABLE),
ord_str(cmp_result).yellow(),
r.style(REMARKABLE),
);
eprintln!("Ord: {cmp_str}");
}
if has_default {
facts.insert(Fact::Default);
let ptr = BoxPtrUninit::new_sized::<T>();
unsafe { shape.call_default_in_place(ptr.ptr) };
let ptr = unsafe { ptr.assume_init() };
let debug = unsafe { debug(shape, ptr.ptr.as_const()) };
eprintln!(
"Default: {}",
format_args!("{debug:?}").style(REMARKABLE)
);
}
if has_clone {
facts.insert(Fact::Clone);
let src_ptr = PtrConst::new(core::ptr::from_ref(val1));
let ptr = BoxPtrUninit::new_sized::<T>();
unsafe { shape.call_clone_into(src_ptr, ptr.ptr.assume_init()) };
let ptr = unsafe { ptr.assume_init() };
let debug = unsafe { debug(shape, ptr.ptr.as_const()) };
eprintln!(
"Clone: {}",
format_args!("{debug:?}").style(REMARKABLE)
);
}
facts
}
#[track_caller]
fn report_maybe_mismatch<'f, 'a, T>(
val1: &'a T,
val2: &'a T,
expected_facts: BTreeSet<Fact>,
facts: BTreeSet<Fact>,
) where
T: Facet<'f> + ?Sized,
{
let name = format!("{}", T::SHAPE);
let expected_minus_actual: BTreeSet<_> = expected_facts.difference(&facts).collect();
let actual_minus_expected: BTreeSet<_> = facts.difference(&expected_facts).collect();
let l = VTableValueView(val1);
let r = VTableValueView(val2);
assert!(
expected_facts == facts,
"{} for {}: ({:?} vs {:?})\n{}\n{}",
"Facts mismatch".red().bold(),
name.style(REMARKABLE),
l.red(),
r.blue(),
expected_minus_actual
.iter()
.map(|f| format!("- {f}"))
.collect::<Vec<_>>()
.join("\n")
.yellow(),
actual_minus_expected
.iter()
.map(|f| format!("+ {f}"))
.collect::<Vec<_>>()
.join("\n")
.yellow(),
);
}
#[track_caller]
fn check_facts<'f, 'a, T>(val1: &'a T, val2: &'a T, expected_facts: BTreeSet<Fact>)
where
T: Facet<'f> + ?Sized,
{
let name = format!("{}", T::SHAPE);
eprint!("{}", format_args!("== {name}: ").yellow());
let facts = collect_facts(val1, val2);
report_maybe_mismatch(val1, val2, expected_facts, facts);
}
#[cfg(feature = "fn-ptr")]
#[track_caller]
fn check_facts_no_cmp<'f, 'a, T>(val1: &'a T, val2: &'a T, expected_facts: BTreeSet<Fact>)
where
T: Facet<'f> + ?Sized,
{
#[cfg(not(miri))]
{
check_facts(val1, val2, expected_facts)
}
#[cfg(miri)]
{
let mut expected_facts = expected_facts;
let name = format!("{}", T::SHAPE);
eprint!("{}", format_args!("== {name}: ").yellow());
let facts = collect_facts(val1, val1);
for &fact in facts.iter() {
if let Fact::PartialEqAnd { .. } | Fact::OrdAnd { .. } | Fact::PartialOrdAnd { .. } =
fact
{
expected_facts.insert(fact);
}
}
report_maybe_mismatch(val1, val2, expected_facts, facts);
}
}
#[derive(Default)]
pub struct FactBuilder {
has_debug: bool,
has_display: bool,
has_partial_eq_and: Option<bool>,
has_ord_and: Option<Ordering>,
has_partial_ord_and: Option<Option<Ordering>>,
has_default: bool,
has_clone: bool,
}
impl FactBuilder {
fn new() -> Self {
Default::default()
}
const fn debug(mut self) -> Self {
self.has_debug = true;
self
}
const fn display(mut self) -> Self {
self.has_display = true;
self
}
const fn partial_eq_and(mut self, l_eq_r: bool) -> Self {
self.has_partial_eq_and = Some(l_eq_r);
self
}
const fn correct_ord_and(self, l_ord_r: Ordering) -> Self {
self.ord_and(l_ord_r).partial_ord_and(Some(l_ord_r))
}
const fn ord_and(mut self, l_ord_r: Ordering) -> Self {
self.has_ord_and = Some(l_ord_r);
self
}
const fn partial_ord_and(mut self, l_ord_r: Option<Ordering>) -> Self {
self.has_partial_ord_and = Some(l_ord_r);
self
}
const fn default(mut self) -> Self {
self.has_default = true;
self
}
const fn clone(mut self) -> Self {
self.has_clone = true;
self
}
fn build(self) -> BTreeSet<Fact> {
let mut facts = BTreeSet::new();
if self.has_debug {
facts.insert(Fact::Debug);
}
if self.has_display {
facts.insert(Fact::Display);
}
if let Some(l_eq_r) = self.has_partial_eq_and {
facts.insert(Fact::PartialEqAnd { l_eq_r });
}
if let Some(l_ord_r) = self.has_ord_and {
facts.insert(Fact::OrdAnd { l_ord_r });
}
if let Some(l_ord_r) = self.has_partial_ord_and {
facts.insert(Fact::PartialOrdAnd { l_ord_r });
}
if self.has_default {
facts.insert(Fact::Default);
}
if self.has_clone {
facts.insert(Fact::Clone);
}
facts
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
enum Fact {
Debug,
Display,
PartialEqAnd { l_eq_r: bool },
OrdAnd { l_ord_r: Ordering },
PartialOrdAnd { l_ord_r: Option<Ordering> },
Default,
Clone,
}
use core::fmt::{Display, Formatter, Result};
impl Display for Fact {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
match self {
Fact::Debug => write!(f, "impl Debug"),
Fact::Display => write!(f, "impl Display"),
Fact::PartialEqAnd { l_eq_r } => write!(
f,
"impl Equal and l {} r",
if *l_eq_r { "==" } else { "!=" }
),
Fact::OrdAnd { l_ord_r } => {
let ord_str = match l_ord_r {
Ordering::Less => "<",
Ordering::Equal => "==",
Ordering::Greater => ">",
};
write!(f, "impl Ord and l {ord_str} r")
}
Fact::PartialOrdAnd { l_ord_r } => {
let ord_str = match l_ord_r {
Some(Ordering::Less) => "<",
Some(Ordering::Equal) => "==",
Some(Ordering::Greater) => ">",
None => "??",
};
write!(f, "impl PartialOrd and l {ord_str} r")
}
Fact::Default => write!(f, "impl Default"),
Fact::Clone => write!(f, "impl Clone"),
}
}
}
#[test]
fn test_integer_traits() {
check_facts(
&42,
&24,
FactBuilder::new()
.debug()
.display()
.partial_eq_and(false)
.correct_ord_and(Ordering::Greater)
.default()
.clone()
.build(),
);
check_facts(
&42,
&42,
FactBuilder::new()
.debug()
.display()
.partial_eq_and(true)
.correct_ord_and(Ordering::Equal)
.default()
.clone()
.build(),
);
check_facts(
&i32::MIN,
&i32::MAX,
FactBuilder::new()
.debug()
.display()
.partial_eq_and(false)
.correct_ord_and(Ordering::Less)
.default()
.clone()
.build(),
);
check_facts(
&0,
&42,
FactBuilder::new()
.debug()
.display()
.partial_eq_and(false)
.correct_ord_and(Ordering::Less)
.default()
.clone()
.build(),
);
check_facts(
&-10,
&10,
FactBuilder::new()
.debug()
.display()
.partial_eq_and(false)
.correct_ord_and(Ordering::Less)
.default()
.clone()
.build(),
);
}
#[test]
fn test_boolean_traits() {
check_facts(
&true,
&false,
FactBuilder::new()
.debug()
.display()
.partial_eq_and(false)
.correct_ord_and(Ordering::Greater)
.default()
.clone()
.build(),
);
check_facts(
&true,
&true,
FactBuilder::new()
.debug()
.display()
.partial_eq_and(true)
.correct_ord_and(Ordering::Equal)
.default()
.clone()
.build(),
);
check_facts(
&false,
&true,
FactBuilder::new()
.debug()
.display()
.partial_eq_and(false)
.correct_ord_and(Ordering::Less)
.default()
.clone()
.build(),
);
check_facts(
&false,
&false,
FactBuilder::new()
.debug()
.display()
.partial_eq_and(true)
.correct_ord_and(Ordering::Equal)
.default()
.clone()
.build(),
);
}
#[test]
fn test_floating_traits() {
check_facts(
&3.18,
&2.71,
FactBuilder::new()
.debug()
.display()
.partial_eq_and(false)
.partial_ord_and(Some(Ordering::Greater))
.default()
.clone()
.build(),
);
check_facts(
&f64::NAN,
&1.0,
FactBuilder::new()
.debug()
.display()
.partial_eq_and(false)
.partial_ord_and(None)
.default()
.clone()
.build(),
);
check_facts(
&f64::NAN,
&f64::NAN,
FactBuilder::new()
.debug()
.display()
.partial_eq_and(false)
.partial_ord_and(None)
.default()
.clone()
.build(),
);
}
#[test]
fn test_string_traits() {
check_facts(
&String::from("hello"),
&String::from("world"),
FactBuilder::new()
.debug()
.display()
.partial_eq_and(false)
.correct_ord_and(Ordering::Less)
.default()
.clone()
.build(),
);
check_facts(
&"hello",
&"world",
FactBuilder::new()
.debug()
.display()
.partial_eq_and(false)
.correct_ord_and(Ordering::Less)
.clone()
.build(),
);
use std::borrow::Cow;
check_facts(
&Cow::Borrowed("hello"),
&Cow::Borrowed("world"),
FactBuilder::new()
.debug()
.display()
.partial_eq_and(false)
.correct_ord_and(Ordering::Less)
.clone()
.default()
.build(),
);
check_facts(
&Cow::<str>::Owned("hello".to_string()),
&Cow::<str>::Owned("world".to_string()),
FactBuilder::new()
.debug()
.display()
.partial_eq_and(false)
.correct_ord_and(Ordering::Less)
.clone()
.default()
.build(),
);
check_facts(
&Cow::Borrowed("same"),
&Cow::Owned("same".to_string()),
FactBuilder::new()
.debug()
.display()
.partial_eq_and(true)
.correct_ord_and(Ordering::Equal)
.clone()
.default()
.build(),
);
}
#[test]
fn test_str_traits() {
check_facts(
"abc",
"abc",
FactBuilder::new()
.debug()
.display()
.partial_eq_and(true)
.correct_ord_and(Ordering::Equal)
.build(),
);
check_facts(
"abc",
"def",
FactBuilder::new()
.debug()
.display()
.partial_eq_and(false)
.correct_ord_and(Ordering::Less)
.build(),
);
let s = String::from("abc");
let s = s.as_str();
check_facts(
s,
s,
FactBuilder::new()
.debug()
.display()
.partial_eq_and(true)
.correct_ord_and(Ordering::Equal)
.build(),
);
}
#[test]
fn test_slice_traits() {
check_facts(
&[1, 2, 3][..],
&[4, 5, 6][..],
FactBuilder::new()
.debug()
.partial_eq_and(false)
.correct_ord_and(Ordering::Less)
.build(),
);
check_facts(
&["hello", "world"][..],
&["foo", "bar"][..],
FactBuilder::new()
.debug()
.partial_eq_and(false)
.correct_ord_and(Ordering::Greater)
.build(),
);
}
#[test]
fn test_slice_ref_traits() {
check_facts(
&&[1, 2, 3][..],
&&[4, 5, 6][..],
FactBuilder::new()
.debug()
.partial_eq_and(false)
.correct_ord_and(Ordering::Less)
.clone()
.build(),
);
check_facts(
&&["hello", "world"][..],
&&["foo", "bar"][..],
FactBuilder::new()
.debug()
.partial_eq_and(false)
.correct_ord_and(Ordering::Greater)
.clone()
.build(),
);
}
#[test]
fn test_array_traits() {
check_facts(
&[42],
&[24],
FactBuilder::new()
.debug()
.partial_eq_and(false)
.correct_ord_and(Ordering::Greater)
.default()
.clone()
.build(),
);
}
#[test]
fn test_vecs() {
check_facts(
&vec![1, 2, 3],
&vec![4, 5, 6],
FactBuilder::new()
.debug()
.partial_eq_and(false)
.correct_ord_and(Ordering::Less)
.default()
.build(),
);
}
#[test]
fn test_custom_structs() {
#[derive(Facet)]
struct StructNoTraits {
value: i32,
}
check_facts(
&StructNoTraits { value: 42 },
&StructNoTraits { value: 24 },
FactBuilder::new().build(),
);
#[derive(Facet, Debug)]
struct StructDebug {
value: i32,
}
check_facts(
&StructDebug { value: 42 },
&StructDebug { value: 24 },
FactBuilder::new().debug().build(),
);
#[derive(Facet, Debug, PartialEq)]
struct StructDebugEq {
value: i32,
}
check_facts(
&StructDebugEq { value: 42 },
&StructDebugEq { value: 24 },
FactBuilder::new().debug().partial_eq_and(false).build(),
);
#[derive(Facet, Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
struct StructAll {
value: i32,
}
check_facts(
&StructAll { value: 42 },
&StructAll { value: 24 },
FactBuilder::new()
.debug()
.partial_eq_and(false)
.correct_ord_and(Ordering::Greater)
.clone()
.build(),
);
check_facts(
&StructAll { value: 10 },
&StructAll { value: 90 },
FactBuilder::new()
.debug()
.partial_eq_and(false)
.correct_ord_and(Ordering::Less)
.clone()
.build(),
);
check_facts(
&StructAll { value: 69 },
&StructAll { value: 69 },
FactBuilder::new()
.debug()
.partial_eq_and(true)
.correct_ord_and(Ordering::Equal)
.clone()
.build(),
);
}
#[test]
fn test_tuple_structs() {
#[derive(Facet)]
#[allow(dead_code)]
struct TupleNoTraits(i32, String);
check_facts(
&TupleNoTraits(42, "Hello".to_string()),
&TupleNoTraits(24, "World".to_string()),
FactBuilder::new().build(),
);
#[derive(Facet, Debug)]
#[allow(dead_code)]
struct TupleDebug(i32, String);
check_facts(
&TupleDebug(42, "Hello".to_string()),
&TupleDebug(24, "World".to_string()),
FactBuilder::new().debug().build(),
);
#[derive(Facet, PartialEq)]
struct TupleEq(i32, String);
check_facts(
&TupleEq(42, "Hello".to_string()),
&TupleEq(24, "World".to_string()),
FactBuilder::new().partial_eq_and(false).build(),
);
#[derive(Facet, Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
struct TupleAll(i32, String);
check_facts(
&TupleAll(42, "Hello".to_string()),
&TupleAll(24, "World".to_string()),
FactBuilder::new()
.debug()
.partial_eq_and(false)
.correct_ord_and(Ordering::Greater)
.clone()
.build(),
);
}
#[test]
fn test_enums() {
#[derive(Facet, Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
#[repr(u8)]
enum TestEnum {
Variant1,
Variant2(i32),
Variant3 { field: String },
}
check_facts(
&TestEnum::Variant1,
&TestEnum::Variant1,
FactBuilder::new()
.debug()
.partial_eq_and(true)
.correct_ord_and(Ordering::Equal)
.clone()
.build(),
);
check_facts(
&TestEnum::Variant2(42),
&TestEnum::Variant2(24),
FactBuilder::new()
.debug()
.partial_eq_and(false)
.correct_ord_and(Ordering::Greater)
.clone()
.build(),
);
check_facts(
&TestEnum::Variant3 {
field: "Hello".to_string(),
},
&TestEnum::Variant3 {
field: "World".to_string(),
},
FactBuilder::new()
.debug()
.partial_eq_and(false)
.correct_ord_and(Ordering::Less)
.clone()
.build(),
);
}
#[test]
fn test_weird_cmp() {
#[derive(Facet)]
struct WeirdCmp;
impl PartialEq for WeirdCmp {
fn eq(&self, _: &Self) -> bool {
false
}
#[allow(clippy::partialeq_ne_impl)]
fn ne(&self, _: &Self) -> bool {
false
}
}
impl Eq for WeirdCmp {}
#[allow(clippy::non_canonical_partial_ord_impl)]
impl PartialOrd for WeirdCmp {
fn partial_cmp(&self, _: &Self) -> Option<Ordering> {
Some(Ordering::Equal)
}
}
impl Ord for WeirdCmp {
fn cmp(&self, _: &Self) -> Ordering {
Ordering::Greater
}
}
check_facts(
&WeirdCmp,
&WeirdCmp,
FactBuilder::new()
.partial_eq_and(false)
.partial_ord_and(Some(Ordering::Equal))
.ord_and(Ordering::Greater)
.build(),
);
}
#[test]
#[cfg(feature = "fn-ptr")]
fn test_fn_ptr() {
let c = |_: u32| -> u32 { 0 };
let c = c as fn(_) -> _;
check_facts_no_cmp::<fn(u32) -> u32>(
&c,
&c,
FactBuilder::new()
.debug()
.partial_eq_and(true)
.correct_ord_and(Ordering::Equal)
.clone()
.build(),
);
extern "C" fn foo(_: usize) -> u32 {
0
}
let foo = foo as extern "C" fn(_) -> _;
check_facts_no_cmp::<extern "C" fn(usize) -> u32>(
&foo,
&foo,
FactBuilder::new()
.debug()
.partial_eq_and(true)
.correct_ord_and(Ordering::Equal)
.clone()
.build(),
);
let l = (|_| 0) as fn(_) -> _;
let r = (|_| 1) as fn(_) -> _;
check_facts_no_cmp::<fn(u32) -> u32>(
&l,
&r,
FactBuilder::new()
.debug()
.partial_eq_and(false)
.correct_ord_and(
#[allow(unpredictable_function_pointer_comparisons)]
l.cmp(&r),
)
.clone()
.build(),
);
}
#[test]
fn test_ptr() {
let unit = ();
let ptr = &raw const unit;
check_facts(
&ptr,
&ptr,
FactBuilder::new()
.clone()
.debug()
.partial_eq_and(true)
.ord_and(Ordering::Equal)
.partial_ord_and(Some(Ordering::Equal))
.build(),
);
check_facts(
&ptr.cast_mut(),
&ptr.cast_mut(),
FactBuilder::new()
.clone()
.debug()
.partial_eq_and(true)
.ord_and(Ordering::Equal)
.partial_ord_and(Some(Ordering::Equal))
.build(),
);
let s = "abc";
let ptr = core::ptr::from_ref(s);
check_facts(
&ptr,
&ptr,
FactBuilder::new()
.clone()
.debug()
.partial_eq_and(true)
.ord_and(Ordering::Equal)
.partial_ord_and(Some(Ordering::Equal))
.build(),
);
check_facts(
&ptr.cast_mut(),
&ptr.cast_mut(),
FactBuilder::new()
.clone()
.debug()
.partial_eq_and(true)
.ord_and(Ordering::Equal)
.partial_ord_and(Some(Ordering::Equal))
.build(),
);
check_facts(
&ptr,
&&raw const s[..1],
FactBuilder::new()
.clone()
.debug()
.partial_eq_and(false)
.correct_ord_and(ptr.cmp(&&raw const s[..1]))
.build(),
);
check_facts(
&ptr.cast_mut(),
&core::ptr::from_ref(&s[..1]).cast_mut(),
FactBuilder::new()
.clone()
.debug()
.partial_eq_and(false)
.correct_ord_and(ptr.cmp(&&raw const s[..1]))
.build(),
);
}
#[test]
fn test_ref() {
check_facts(
&&(),
&&(),
FactBuilder::new()
.debug()
.partial_eq_and(true)
.ord_and(Ordering::Equal)
.partial_ord_and(Some(Ordering::Equal))
.clone()
.build(),
);
let unit = ();
let ptr = &raw const unit;
check_facts(
&&ptr,
&&ptr,
FactBuilder::new()
.debug()
.partial_eq_and(true)
.ord_and(Ordering::Equal)
.partial_ord_and(Some(Ordering::Equal))
.clone()
.build(),
);
}
#[test]
fn test_mut_ref() {
check_facts(
&&mut (),
&&mut (),
FactBuilder::new()
.debug()
.partial_eq_and(true)
.ord_and(Ordering::Equal)
.partial_ord_and(Some(Ordering::Equal))
.build(),
);
let unit = ();
let mut ptr1 = &raw const unit;
let mut ptr2 = &raw const unit;
let ref1 = &mut ptr1;
let ref2 = &mut ptr2;
check_facts(
&ref1,
&ref2,
FactBuilder::new()
.debug()
.partial_eq_and(true)
.ord_and(Ordering::Equal)
.partial_ord_and(Some(Ordering::Equal))
.build(),
);
}
#[test]
fn test_rc_weak() {
let v = Rc::new(());
let mut w1 = Rc::downgrade(&v);
let mut w2 = Rc::downgrade(&v);
check_facts(
&w1,
&w2,
FactBuilder::new().clone().debug().default().build(),
);
check_facts(&&w1, &&w2, FactBuilder::new().clone().debug().build());
check_facts(&&mut w1, &&mut w2, FactBuilder::new().debug().build());
let ptr = &raw const w1;
check_facts(
&ptr,
&ptr,
FactBuilder::new()
.clone()
.debug()
.partial_eq_and(true)
.correct_ord_and(Ordering::Equal)
.build(),
);
check_facts(
&ptr.cast_mut(),
&ptr.cast_mut(),
FactBuilder::new()
.clone()
.debug()
.partial_eq_and(true)
.correct_ord_and(Ordering::Equal)
.build(),
);
}
#[test]
#[cfg(feature = "net")]
fn test_ipv4_addr_parse_from_str() {
use core::net::Ipv4Addr;
use facet_reflect::Partial;
let wip = Partial::alloc::<Ipv4Addr>().unwrap();
let wip = wip
.parse_from_str("127.0.0.1")
.expect("Failed to parse valid IP address");
let value: Ipv4Addr = wip.build().unwrap().materialize().unwrap();
assert_eq!(value, "127.0.0.1".parse::<Ipv4Addr>().unwrap());
let wip2 = Partial::alloc::<Ipv4Addr>().unwrap();
let result2 = wip2.parse_from_str("not.an.ip.address");
assert!(result2.is_err(), "Should fail to parse invalid IP address");
let shape = Ipv4Addr::SHAPE;
assert!(
shape.is_from_str(),
"Ipv4Addr should support parsing from string"
);
assert!(
shape.vtable.has_parse(),
"Ipv4Addr should have a parse function in vtable"
);
}
#[test]
#[cfg(feature = "num-complex")]
fn test_complex_default() {
use num_complex::Complex;
check_facts(
&Complex::new(1.0f64, 2.0f64),
&Complex::new(3.0f64, 4.0f64),
FactBuilder::new()
.debug()
.display()
.partial_eq_and(false)
.default()
.build(),
);
check_facts(
&Complex::new(1.0f32, 2.0f32),
&Complex::new(1.0f32, 2.0f32),
FactBuilder::new()
.debug()
.display()
.partial_eq_and(true)
.default()
.build(),
);
}
#[test]
fn test_ref_clone_debug() {
let shape = <&()>::SHAPE;
eprintln!("Shape for &(): {}", shape);
let has_clone = shape
.type_ops
.map(|ops| ops.has_clone_into())
.unwrap_or(false);
eprintln!("type_ops has_clone_into: {}", has_clone);
let unit = ();
let ref_to_unit: &() = &unit;
let ptr = PtrConst::new(&ref_to_unit as *const &());
let mut dst: std::mem::MaybeUninit<&()> = std::mem::MaybeUninit::uninit();
let dst_ptr = PtrMut::new(dst.as_mut_ptr());
let result = unsafe { shape.call_clone_into(ptr, dst_ptr) };
eprintln!("call_clone_into result: {:?}", result);
assert!(
shape
.type_ops
.map(|ops| ops.has_clone_into())
.unwrap_or(false),
"REF type_ops should have clone_into"
);
assert!(result.is_some(), "clone_into should succeed");
}