use core::{cmp::Ordering, marker::PhantomData, ptr::NonNull};
#[cfg(feature = "alloc")]
use facet_core::Field;
use facet_core::{
Def, Facet, PointerType, PtrConst, Shape, StructKind, Type, TypeNameOpts, UserType,
VTableErased, Variance,
};
use crate::{PeekNdArray, PeekSet, ReflectError, ReflectErrorKind, ScalarType};
use facet_path::{Path, PathAccessError, PathStep};
use super::{
ListLikeDef, PeekDynamicValue, PeekEnum, PeekList, PeekListLike, PeekMap, PeekOption,
PeekPointer, PeekResult, PeekStruct, PeekTuple, tuple::TupleType,
};
#[cfg(feature = "alloc")]
use super::OwnedPeek;
#[derive(Clone, Copy, PartialEq, PartialOrd, Ord, Eq, Hash)]
pub struct ValueId {
pub(crate) shape: &'static Shape,
pub(crate) ptr: *const u8,
}
impl ValueId {
#[inline]
pub(crate) const fn new(shape: &'static Shape, ptr: *const u8) -> Self {
Self { shape, ptr }
}
}
impl core::fmt::Display for ValueId {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}@{:p}", self.shape, self.ptr)
}
}
impl core::fmt::Debug for ValueId {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::fmt::Display::fmt(self, f)
}
}
#[allow(clippy::type_complexity)]
#[derive(Clone, Copy)]
pub struct Peek<'mem, 'facet> {
pub(crate) data: PtrConst,
pub(crate) shape: &'static Shape,
_invariant: PhantomData<(&'mem (), fn(&'facet ()) -> &'facet ())>,
}
impl<'mem, 'facet> Peek<'mem, 'facet> {
pub fn new<T: Facet<'facet> + ?Sized>(t: &'mem T) -> Self {
Self {
data: PtrConst::new(NonNull::from(t).as_ptr()),
shape: T::SHAPE,
_invariant: PhantomData,
}
}
#[inline]
pub(crate) fn err(&self, kind: ReflectErrorKind) -> ReflectError {
ReflectError::new(kind, Path::new(self.shape))
}
pub unsafe fn unchecked_new(data: PtrConst, shape: &'static Shape) -> Self {
Self {
data,
shape,
_invariant: PhantomData,
}
}
#[inline]
pub fn variance(&self) -> Variance {
self.shape.computed_variance()
}
#[inline]
pub fn shrink_lifetime<'shorter>(self) -> Peek<'mem, 'shorter>
where
'facet: 'shorter,
{
self.try_shrink_lifetime()
.expect("shrink_lifetime requires a covariant type")
}
#[inline]
pub fn try_shrink_lifetime<'shorter>(self) -> Option<Peek<'mem, 'shorter>>
where
'facet: 'shorter,
{
if self.variance().can_shrink() {
Some(Peek {
data: self.data,
shape: self.shape,
_invariant: PhantomData,
})
} else {
None
}
}
#[inline]
pub fn grow_lifetime<'longer>(self) -> Peek<'mem, 'longer>
where
'longer: 'facet,
{
self.try_grow_lifetime()
.expect("grow_lifetime requires a contravariant type")
}
#[inline]
pub fn try_grow_lifetime<'longer>(self) -> Option<Peek<'mem, 'longer>>
where
'longer: 'facet,
{
if self.variance().can_grow() {
Some(Peek {
data: self.data,
shape: self.shape,
_invariant: PhantomData,
})
} else {
None
}
}
#[inline(always)]
pub const fn vtable(&self) -> VTableErased {
self.shape.vtable
}
#[inline]
pub fn id(&self) -> ValueId {
ValueId::new(self.shape, self.data.raw_ptr())
}
#[inline]
pub fn ptr_eq(&self, other: &Peek<'_, '_>) -> bool {
self.data.raw_ptr() == other.data.raw_ptr()
}
#[inline]
pub fn partial_eq(&self, other: &Peek<'_, '_>) -> Result<bool, ReflectError> {
if self.shape != other.shape {
return Err(self.err(ReflectErrorKind::WrongShape {
expected: self.shape,
actual: other.shape,
}));
}
if let Some(result) = unsafe { self.shape.call_partial_eq(self.data, other.data) } {
return Ok(result);
}
Err(self.err(ReflectErrorKind::OperationFailed {
shape: self.shape(),
operation: "partial_eq",
}))
}
#[inline]
pub fn partial_cmp(&self, other: &Peek<'_, '_>) -> Result<Option<Ordering>, ReflectError> {
if self.shape != other.shape {
return Err(self.err(ReflectErrorKind::WrongShape {
expected: self.shape,
actual: other.shape,
}));
}
if let Some(result) = unsafe { self.shape.call_partial_cmp(self.data, other.data) } {
return Ok(result);
}
Err(self.err(ReflectErrorKind::OperationFailed {
shape: self.shape(),
operation: "partial_cmp",
}))
}
#[inline(always)]
pub fn hash(&self, hasher: &mut dyn core::hash::Hasher) -> Result<(), ReflectError> {
let mut proxy = facet_core::HashProxy::new(hasher);
if unsafe { self.shape.call_hash(self.data, &mut proxy) }.is_some() {
return Ok(());
}
Err(self.err(ReflectErrorKind::OperationFailed {
shape: self.shape(),
operation: "hash",
}))
}
pub fn structural_hash<H: core::hash::Hasher>(&self, hasher: &mut H) {
use core::hash::Hash;
self.shape.id.hash(hasher);
let mut proxy = facet_core::HashProxy::new(hasher);
if unsafe { self.shape.call_hash(self.data, &mut proxy) }.is_some() {
return;
}
match self.shape.ty {
Type::User(UserType::Struct(struct_type)) => {
(struct_type.kind as u8).hash(hasher);
for field in struct_type.fields {
if field.is_metadata() {
continue;
}
field.name.hash(hasher);
let field_offset = field.offset;
let field_shape = field.shape();
let field_ptr = unsafe { self.data.field(field_offset) };
let field_peek = unsafe { Peek::unchecked_new(field_ptr, field_shape) };
field_peek.structural_hash(hasher);
}
}
Type::User(UserType::Enum(_enum_type)) => {
if let Ok(peek_enum) = self.into_enum()
&& let Ok(variant) = peek_enum.active_variant()
{
variant.name.hash(hasher);
match variant.data.kind {
StructKind::Unit => {
}
StructKind::TupleStruct | StructKind::Tuple => {
use super::HasFields;
for (_field, peek) in peek_enum.fields() {
peek.structural_hash(hasher);
}
}
StructKind::Struct => {
use super::HasFields;
for (field, peek) in peek_enum.fields() {
field.name.hash(hasher);
peek.structural_hash(hasher);
}
}
}
}
}
_ => {
match self.shape.def {
Def::List(_) | Def::Array(_) | Def::Slice(_) => {
if let Ok(list_like) = self.into_list_like() {
list_like.len().hash(hasher);
for elem in list_like.iter() {
elem.structural_hash(hasher);
}
}
}
Def::Map(_) => {
if let Ok(map) = self.into_map() {
map.len().hash(hasher);
for (key, value) in map.iter() {
key.structural_hash(hasher);
value.structural_hash(hasher);
}
}
}
Def::Set(_) => {
if let Ok(set) = self.into_set() {
set.len().hash(hasher);
for elem in set.iter() {
elem.structural_hash(hasher);
}
}
}
Def::Option(_) => {
if let Ok(opt) = self.into_option() {
if let Some(inner) = opt.value() {
true.hash(hasher);
inner.structural_hash(hasher);
} else {
false.hash(hasher);
}
}
}
Def::Result(_) => {
if let Ok(result) = self.into_result() {
if result.is_ok() {
0u8.hash(hasher);
if let Some(ok_val) = result.ok() {
ok_val.structural_hash(hasher);
}
} else {
1u8.hash(hasher);
if let Some(err_val) = result.err() {
err_val.structural_hash(hasher);
}
}
}
}
Def::Pointer(_) => {
if let Ok(ptr) = self.into_pointer()
&& let Some(inner) = ptr.borrow_inner()
{
inner.structural_hash(hasher);
}
}
Def::DynamicValue(_) => {
if let Ok(dyn_val) = self.into_dynamic_value() {
dyn_val.structural_hash_inner(hasher);
}
}
Def::NdArray(_) => {
if let Ok(arr) = self.into_ndarray() {
let n_dim = arr.n_dim();
n_dim.hash(hasher);
for i in 0..n_dim {
if let Some(dim) = arr.dim(i) {
dim.hash(hasher);
}
}
let count = arr.count();
for i in 0..count {
if let Some(elem) = arr.get(i) {
elem.structural_hash(hasher);
}
}
}
}
Def::Scalar | Def::Undefined | _ => {
match self.scalar_type() {
Some(ScalarType::F32) => {
if let Ok(v) = self.get::<f32>() {
v.to_bits().hash(hasher);
return;
}
}
Some(ScalarType::F64) => {
if let Ok(v) = self.get::<f64>() {
v.to_bits().hash(hasher);
return;
}
}
_ => {}
}
panic!(
"structural_hash: type {} has no Hash impl and cannot be structurally hashed",
self.shape
);
}
}
}
}
}
#[inline(always)]
pub fn type_name(
&self,
f: &mut core::fmt::Formatter<'_>,
opts: TypeNameOpts,
) -> core::fmt::Result {
self.shape.write_type_name(f, opts)
}
#[inline(always)]
pub const fn shape(&self) -> &'static Shape {
self.shape
}
#[inline(always)]
pub const fn data(&self) -> PtrConst {
self.data
}
#[inline]
pub fn scalar_type(&self) -> Option<ScalarType> {
ScalarType::try_from_shape(self.shape)
}
#[inline]
pub fn get<T: Facet<'facet> + ?Sized>(&self) -> Result<&'mem T, ReflectError> {
if self.shape != T::SHAPE {
Err(self.err(ReflectErrorKind::WrongShape {
expected: self.shape,
actual: T::SHAPE,
}))
} else {
Ok(unsafe { self.data.get::<T>() })
}
}
pub fn as_str(&self) -> Option<&'mem str> {
let peek = self.innermost_peek();
if let Some(ScalarType::Str) = peek.scalar_type()
&& !matches!(peek.shape.ty, Type::Pointer(_))
{
return unsafe { Some(peek.data.get::<str>()) };
}
#[cfg(feature = "alloc")]
if let Some(ScalarType::String) = peek.scalar_type() {
return unsafe { Some(peek.data.get::<alloc::string::String>().as_str()) };
}
#[cfg(feature = "alloc")]
if let Some(ScalarType::CowStr) = peek.scalar_type() {
return unsafe { Some(peek.data.get::<alloc::borrow::Cow<'mem, str>>().as_ref()) };
}
if let Type::Pointer(PointerType::Reference(vpt)) = peek.shape.ty {
let target_shape = vpt.target;
if let Type::Pointer(PointerType::Reference(inner_vpt)) = target_shape.ty {
let inner_target_shape = inner_vpt.target;
if let Some(ScalarType::Str) = ScalarType::try_from_shape(inner_target_shape) {
let outer_ptr: *const *const &str =
unsafe { peek.data.as_ptr::<*const &str>() };
let inner_ref: &str = unsafe { **outer_ptr };
return Some(inner_ref);
}
} else if let Some(ScalarType::Str) = ScalarType::try_from_shape(target_shape)
&& !matches!(target_shape.ty, Type::Pointer(_))
{
return unsafe { Some(peek.data.get::<&str>()) };
}
}
#[cfg(feature = "alloc")]
if let Def::Pointer(ptr_def) = peek.shape.def
&& let Some(pointee_shape) = ptr_def.pointee
&& let Some(ScalarType::Str) = ScalarType::try_from_shape(pointee_shape)
&& let Some(borrow_fn) = ptr_def.vtable.borrow_fn
{
let inner_ptr = unsafe { borrow_fn(peek.data) };
return unsafe { Some(inner_ptr.get::<str>()) };
}
None
}
#[inline]
pub fn as_bytes(&self) -> Option<&'mem [u8]> {
if let Type::Pointer(PointerType::Reference(vpt)) = self.shape.ty {
let target_shape = vpt.target;
if let Def::Slice(sd) = target_shape.def
&& sd.t().is_type::<u8>()
{
unsafe { return Some(self.data.get::<&[u8]>()) }
}
}
None
}
#[inline]
pub fn into_struct(self) -> Result<PeekStruct<'mem, 'facet>, ReflectError> {
if let Type::User(UserType::Struct(ty)) = self.shape.ty {
Ok(PeekStruct { value: self, ty })
} else {
Err(self.err(ReflectErrorKind::WasNotA {
expected: "struct",
actual: self.shape,
}))
}
}
#[inline]
pub fn into_enum(self) -> Result<PeekEnum<'mem, 'facet>, ReflectError> {
if let Type::User(UserType::Enum(ty)) = self.shape.ty {
Ok(PeekEnum { value: self, ty })
} else {
Err(self.err(ReflectErrorKind::WasNotA {
expected: "enum",
actual: self.shape,
}))
}
}
#[inline]
pub fn into_map(self) -> Result<PeekMap<'mem, 'facet>, ReflectError> {
if let Def::Map(def) = self.shape.def {
Ok(unsafe { PeekMap::new(self, def) })
} else {
Err(self.err(ReflectErrorKind::WasNotA {
expected: "map",
actual: self.shape,
}))
}
}
#[inline]
pub fn into_set(self) -> Result<PeekSet<'mem, 'facet>, ReflectError> {
if let Def::Set(def) = self.shape.def {
Ok(unsafe { PeekSet::new(self, def) })
} else {
Err(self.err(ReflectErrorKind::WasNotA {
expected: "set",
actual: self.shape,
}))
}
}
#[inline]
pub fn into_list(self) -> Result<PeekList<'mem, 'facet>, ReflectError> {
if let Def::List(def) = self.shape.def {
return Ok(unsafe { PeekList::new(self, def) });
}
Err(self.err(ReflectErrorKind::WasNotA {
expected: "list",
actual: self.shape,
}))
}
#[inline]
pub fn into_ndarray(self) -> Result<PeekNdArray<'mem, 'facet>, ReflectError> {
if let Def::NdArray(def) = self.shape.def {
return Ok(unsafe { PeekNdArray::new(self, def) });
}
Err(self.err(ReflectErrorKind::WasNotA {
expected: "ndarray",
actual: self.shape,
}))
}
#[inline]
pub fn into_list_like(self) -> Result<PeekListLike<'mem, 'facet>, ReflectError> {
match self.shape.def {
Def::List(def) => {
Ok(unsafe { PeekListLike::new(self, ListLikeDef::List(def)) })
}
Def::Array(def) => {
Ok(unsafe { PeekListLike::new(self, ListLikeDef::Array(def)) })
}
Def::Slice(def) => {
Ok(unsafe { PeekListLike::new(self, ListLikeDef::Slice(def)) })
}
_ => {
match self.shape.ty {
Type::Pointer(ptr) => match ptr {
PointerType::Reference(vpt) | PointerType::Raw(vpt) => {
let target = vpt.target;
match target.def {
Def::Slice(def) => {
let ptr = unsafe { self.data.as_ptr::<*const [()]>() };
let ptr = PtrConst::new(unsafe {
NonNull::new_unchecked((*ptr) as *mut [()]).as_ptr()
});
let peek = unsafe { Peek::unchecked_new(ptr, def.t) };
return Ok(unsafe {
PeekListLike::new(peek, ListLikeDef::Slice(def))
});
}
_ => {
}
}
}
PointerType::Function(_) => {
}
},
_ => {
}
}
Err(self.err(ReflectErrorKind::WasNotA {
expected: "list, array or slice",
actual: self.shape,
}))
}
}
}
#[inline]
pub fn into_pointer(self) -> Result<PeekPointer<'mem, 'facet>, ReflectError> {
if let Def::Pointer(def) = self.shape.def {
Ok(PeekPointer { value: self, def })
} else {
Err(self.err(ReflectErrorKind::WasNotA {
expected: "smart pointer",
actual: self.shape,
}))
}
}
#[inline]
pub fn into_option(self) -> Result<PeekOption<'mem, 'facet>, ReflectError> {
if let Def::Option(def) = self.shape.def {
Ok(PeekOption { value: self, def })
} else {
Err(self.err(ReflectErrorKind::WasNotA {
expected: "option",
actual: self.shape,
}))
}
}
#[inline]
pub fn into_result(self) -> Result<PeekResult<'mem, 'facet>, ReflectError> {
if let Def::Result(def) = self.shape.def {
Ok(PeekResult { value: self, def })
} else {
Err(self.err(ReflectErrorKind::WasNotA {
expected: "result",
actual: self.shape,
}))
}
}
#[inline]
pub fn into_tuple(self) -> Result<PeekTuple<'mem, 'facet>, ReflectError> {
if let Type::User(UserType::Struct(struct_type)) = self.shape.ty {
if struct_type.kind == StructKind::Tuple {
Ok(PeekTuple {
value: self,
ty: TupleType {
fields: struct_type.fields,
},
})
} else {
Err(self.err(ReflectErrorKind::WasNotA {
expected: "tuple",
actual: self.shape,
}))
}
} else {
Err(self.err(ReflectErrorKind::WasNotA {
expected: "tuple",
actual: self.shape,
}))
}
}
#[inline]
pub fn into_dynamic_value(self) -> Result<PeekDynamicValue<'mem, 'facet>, ReflectError> {
if let Def::DynamicValue(def) = self.shape.def {
Ok(PeekDynamicValue { value: self, def })
} else {
Err(self.err(ReflectErrorKind::WasNotA {
expected: "dynamic value",
actual: self.shape,
}))
}
}
pub fn innermost_peek(self) -> Self {
let mut current_peek = self;
loop {
if let Ok(ptr) = current_peek.into_pointer()
&& let Some(target) = ptr.borrow_inner()
{
current_peek = target;
continue;
}
if let Some(inner_shape) = current_peek.shape.inner {
let result = unsafe { current_peek.shape.call_try_borrow_inner(current_peek.data) };
match result {
Some(Ok(inner_data)) => {
current_peek = Peek {
data: inner_data.as_const(),
shape: inner_shape,
_invariant: PhantomData,
};
continue;
}
Some(Err(e)) => {
panic!(
"innermost_peek: try_borrow_inner returned an error! was trying to go from {} to {}. error: {e}",
current_peek.shape, inner_shape
);
}
None => {
}
}
}
break;
}
current_peek
}
#[cfg(feature = "alloc")]
pub fn custom_serialization(&self, field: Field) -> Result<OwnedPeek<'mem>, ReflectError> {
let Some(proxy_def) = field.proxy() else {
return Err(self.err(ReflectErrorKind::OperationFailed {
shape: self.shape,
operation: "field does not have a proxy definition",
}));
};
let target_shape = proxy_def.shape;
let tptr = target_shape.allocate().map_err(|_| {
self.err(ReflectErrorKind::Unsized {
shape: target_shape,
operation: "Not a Sized type",
})
})?;
let ser_res = unsafe { (proxy_def.convert_out)(self.data(), tptr) };
let err = match ser_res {
Ok(rptr) => {
if rptr.as_uninit() != tptr {
ReflectErrorKind::CustomSerializationError {
message: "convert_out did not return the expected pointer".into(),
src_shape: self.shape,
dst_shape: target_shape,
}
} else {
return Ok(OwnedPeek {
shape: target_shape,
data: rptr,
_phantom: PhantomData,
});
}
}
Err(message) => ReflectErrorKind::CustomSerializationError {
message,
src_shape: self.shape,
dst_shape: target_shape,
},
};
unsafe {
target_shape.deallocate_uninit(tptr).unwrap()
};
Err(self.err(err))
}
#[cfg(feature = "alloc")]
pub fn custom_serialization_with_proxy(
&self,
proxy_def: &'static facet_core::ProxyDef,
) -> Result<OwnedPeek<'mem>, ReflectError> {
let target_shape = proxy_def.shape;
let tptr = target_shape.allocate().map_err(|_| {
self.err(ReflectErrorKind::Unsized {
shape: target_shape,
operation: "Not a Sized type",
})
})?;
let ser_res = unsafe { (proxy_def.convert_out)(self.data(), tptr) };
let err = match ser_res {
Ok(rptr) => {
if rptr.as_uninit() != tptr {
ReflectErrorKind::CustomSerializationError {
message: "convert_out did not return the expected pointer".into(),
src_shape: self.shape,
dst_shape: target_shape,
}
} else {
return Ok(OwnedPeek {
shape: target_shape,
data: rptr,
_phantom: PhantomData,
});
}
}
Err(message) => ReflectErrorKind::CustomSerializationError {
message,
src_shape: self.shape,
dst_shape: target_shape,
},
};
unsafe {
target_shape.deallocate_uninit(tptr).unwrap()
};
Err(self.err(err))
}
#[cfg(feature = "alloc")]
pub fn custom_serialization_from_shape(&self) -> Result<Option<OwnedPeek<'mem>>, ReflectError> {
self.custom_serialization_from_shape_with_format(None)
}
#[cfg(feature = "alloc")]
pub fn custom_serialization_from_shape_with_format(
&self,
format_namespace: Option<&str>,
) -> Result<Option<OwnedPeek<'mem>>, ReflectError> {
let Some(proxy_def) = self.shape.effective_proxy(format_namespace) else {
return Ok(None);
};
let target_shape = proxy_def.shape;
let tptr = target_shape.allocate().map_err(|_| {
self.err(ReflectErrorKind::Unsized {
shape: target_shape,
operation: "Not a Sized type",
})
})?;
let ser_res = unsafe { (proxy_def.convert_out)(self.data(), tptr) };
let err = match ser_res {
Ok(rptr) => {
if rptr.as_uninit() != tptr {
ReflectErrorKind::CustomSerializationError {
message: "proxy convert_out did not return the expected pointer".into(),
src_shape: self.shape,
dst_shape: target_shape,
}
} else {
return Ok(Some(OwnedPeek {
shape: target_shape,
data: rptr,
_phantom: PhantomData,
}));
}
}
Err(message) => ReflectErrorKind::CustomSerializationError {
message,
src_shape: self.shape,
dst_shape: target_shape,
},
};
unsafe {
target_shape.deallocate_uninit(tptr).unwrap()
};
Err(self.err(err))
}
pub fn at_path(self, path: &Path) -> Result<Peek<'mem, 'facet>, PathAccessError> {
if self.shape != path.shape {
return Err(PathAccessError::RootShapeMismatch {
expected: path.shape,
actual: self.shape,
});
}
let mut current = self;
for (step_index, step) in path.steps().iter().enumerate() {
current = current.apply_step(*step, step_index)?;
}
Ok(current)
}
fn apply_step(
self,
step: PathStep,
step_index: usize,
) -> Result<Peek<'mem, 'facet>, PathAccessError> {
match step {
PathStep::Field(idx) => {
let idx = idx as usize;
match self.shape.ty {
Type::User(UserType::Struct(sd)) => {
if idx >= sd.fields.len() {
return Err(PathAccessError::IndexOutOfBounds {
step,
step_index,
shape: self.shape,
index: idx,
bound: sd.fields.len(),
});
}
let field = &sd.fields[idx];
let field_data = unsafe { self.data.field(field.offset) };
Ok(unsafe { Peek::unchecked_new(field_data, field.shape()) })
}
Type::User(UserType::Enum(_)) => {
let peek_enum =
self.into_enum()
.map_err(|_| PathAccessError::WrongStepKind {
step,
step_index,
shape: self.shape,
})?;
let variant = peek_enum.active_variant().map_err(|_| {
PathAccessError::WrongStepKind {
step,
step_index,
shape: self.shape,
}
})?;
if idx >= variant.data.fields.len() {
return Err(PathAccessError::IndexOutOfBounds {
step,
step_index,
shape: self.shape,
index: idx,
bound: variant.data.fields.len(),
});
}
peek_enum
.field(idx)
.map_err(|_| PathAccessError::WrongStepKind {
step,
step_index,
shape: self.shape,
})?
.ok_or(PathAccessError::IndexOutOfBounds {
step,
step_index,
shape: self.shape,
index: idx,
bound: variant.data.fields.len(),
})
}
_ => Err(PathAccessError::WrongStepKind {
step,
step_index,
shape: self.shape,
}),
}
}
PathStep::Variant(expected_idx) => {
let expected_idx = expected_idx as usize;
let peek_enum = self
.into_enum()
.map_err(|_| PathAccessError::WrongStepKind {
step,
step_index,
shape: self.shape,
})?;
if expected_idx >= peek_enum.variants().len() {
return Err(PathAccessError::IndexOutOfBounds {
step,
step_index,
shape: self.shape,
index: expected_idx,
bound: peek_enum.variants().len(),
});
}
let actual_idx =
peek_enum
.variant_index()
.map_err(|_| PathAccessError::WrongStepKind {
step,
step_index,
shape: self.shape,
})?;
if actual_idx != expected_idx {
return Err(PathAccessError::VariantMismatch {
step_index,
shape: self.shape,
expected_variant: expected_idx,
actual_variant: actual_idx,
});
}
Ok(self)
}
PathStep::Index(idx) => {
let idx = idx as usize;
match self.shape.def {
Def::List(def) => {
let list = unsafe { super::PeekList::new(self, def) };
let len = list.len();
list.get(idx).ok_or(PathAccessError::IndexOutOfBounds {
step,
step_index,
shape: self.shape,
index: idx,
bound: len,
})
}
Def::Array(def) => {
let list_like =
unsafe { super::PeekListLike::new(self, ListLikeDef::Array(def)) };
let len = list_like.len();
list_like.get(idx).ok_or(PathAccessError::IndexOutOfBounds {
step,
step_index,
shape: self.shape,
index: idx,
bound: len,
})
}
_ => Err(PathAccessError::WrongStepKind {
step,
step_index,
shape: self.shape,
}),
}
}
PathStep::MapKey(entry_idx) => {
let entry_idx = entry_idx as usize;
if let Def::Map(def) = self.shape.def {
let map = unsafe { super::PeekMap::new(self, def) };
let len = map.len();
if entry_idx >= len {
return Err(PathAccessError::IndexOutOfBounds {
step,
step_index,
shape: self.shape,
index: entry_idx,
bound: len,
});
}
for (i, (key, _value)) in map.iter().enumerate() {
if i == entry_idx {
return Ok(key);
}
}
Err(PathAccessError::IndexOutOfBounds {
step,
step_index,
shape: self.shape,
index: entry_idx,
bound: len,
})
} else {
Err(PathAccessError::WrongStepKind {
step,
step_index,
shape: self.shape,
})
}
}
PathStep::MapValue(entry_idx) => {
let entry_idx = entry_idx as usize;
if let Def::Map(def) = self.shape.def {
let map = unsafe { super::PeekMap::new(self, def) };
let len = map.len();
if entry_idx >= len {
return Err(PathAccessError::IndexOutOfBounds {
step,
step_index,
shape: self.shape,
index: entry_idx,
bound: len,
});
}
for (i, (_key, value)) in map.iter().enumerate() {
if i == entry_idx {
return Ok(value);
}
}
Err(PathAccessError::IndexOutOfBounds {
step,
step_index,
shape: self.shape,
index: entry_idx,
bound: len,
})
} else {
Err(PathAccessError::WrongStepKind {
step,
step_index,
shape: self.shape,
})
}
}
PathStep::OptionSome => {
if let Def::Option(def) = self.shape.def {
let opt = PeekOption { value: self, def };
opt.value().ok_or(PathAccessError::OptionIsNone {
step_index,
shape: self.shape,
})
} else {
Err(PathAccessError::WrongStepKind {
step,
step_index,
shape: self.shape,
})
}
}
PathStep::Deref => {
if let Def::Pointer(def) = self.shape.def {
let ptr = PeekPointer { value: self, def };
ptr.borrow_inner().ok_or(PathAccessError::MissingTarget {
step,
step_index,
shape: self.shape,
})
} else {
Err(PathAccessError::WrongStepKind {
step,
step_index,
shape: self.shape,
})
}
}
PathStep::Inner => {
let inner_shape = self.shape.inner.ok_or(PathAccessError::MissingTarget {
step,
step_index,
shape: self.shape,
})?;
let result = unsafe { self.shape.call_try_borrow_inner(self.data) };
match result {
Some(Ok(inner_data)) => Ok(Peek {
data: inner_data.as_const(),
shape: inner_shape,
_invariant: PhantomData,
}),
_ => Err(PathAccessError::MissingTarget {
step,
step_index,
shape: self.shape,
}),
}
}
PathStep::Proxy => {
let proxy_def =
self.shape
.effective_proxy(None)
.ok_or(PathAccessError::MissingTarget {
step,
step_index,
shape: self.shape,
})?;
Err(PathAccessError::MissingTarget {
step,
step_index,
shape: proxy_def.shape,
})
}
}
}
}
impl<'mem, 'facet> core::fmt::Display for Peek<'mem, 'facet> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
if let Some(result) = unsafe { self.shape.call_display(self.data, f) } {
return result;
}
write!(f, "⟨{}⟩", self.shape)
}
}
impl<'mem, 'facet> core::fmt::Debug for Peek<'mem, 'facet> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
if let Some(result) = unsafe { self.shape.call_debug(self.data, f) } {
return result;
}
write!(f, "⟨{}⟩", self.shape)
}
}
impl<'mem, 'facet> core::cmp::PartialEq for Peek<'mem, 'facet> {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.partial_eq(other).unwrap_or(false)
}
}
impl<'mem, 'facet> core::cmp::PartialOrd for Peek<'mem, 'facet> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
self.partial_cmp(other).unwrap_or(None)
}
}
impl<'mem, 'facet> core::hash::Hash for Peek<'mem, 'facet> {
fn hash<H: core::hash::Hasher>(&self, hasher: &mut H) {
self.hash(hasher)
.expect("Hashing is not supported for this shape");
}
}
#[derive(Clone, Copy)]
pub struct CovariantPeek<'mem, 'facet> {
data: PtrConst,
shape: &'static Shape,
_covariant: PhantomData<(&'mem (), &'facet ())>,
}
impl<'mem, 'facet> CovariantPeek<'mem, 'facet> {
#[inline]
pub fn new(peek: Peek<'mem, 'facet>) -> Option<Self> {
if peek.variance().can_shrink() {
Some(Self {
data: peek.data,
shape: peek.shape,
_covariant: PhantomData,
})
} else {
None
}
}
#[inline]
pub fn new_unchecked(peek: Peek<'mem, 'facet>) -> Self {
Self::new(peek).unwrap_or_else(|| {
panic!(
"CovariantPeek::new_unchecked called on type that cannot shrink lifetimes: {} (variance: {:?})",
peek.shape,
peek.variance()
)
})
}
#[inline]
pub fn from_ref<T: Facet<'facet> + ?Sized>(t: &'mem T) -> Option<Self> {
Self::new(Peek::new(t))
}
#[inline]
pub fn into_peek(self) -> Peek<'mem, 'facet> {
Peek {
data: self.data,
shape: self.shape,
_invariant: PhantomData,
}
}
#[inline]
pub const fn shape(&self) -> &'static Shape {
self.shape
}
#[inline]
pub const fn data(&self) -> PtrConst {
self.data
}
}
impl<'mem, 'facet> core::ops::Deref for CovariantPeek<'mem, 'facet> {
type Target = Peek<'mem, 'facet>;
#[inline]
fn deref(&self) -> &Self::Target {
unsafe { &*(self as *const CovariantPeek<'mem, 'facet> as *const Peek<'mem, 'facet>) }
}
}
impl<'mem, 'facet> core::fmt::Debug for CovariantPeek<'mem, 'facet> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("CovariantPeek")
.field("shape", &self.shape)
.field("data", &self.data)
.finish()
}
}
impl<'mem, 'facet> core::fmt::Display for CovariantPeek<'mem, 'facet> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::fmt::Display::fmt(&**self, f)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_peek_as_str_empty_string() {
let peek = Peek::new("");
assert_eq!(peek.as_str(), Some(""));
}
#[test]
fn test_peek_as_str_non_empty_string() {
let peek = Peek::new("hello");
assert_eq!(peek.as_str(), Some("hello"));
}
#[test]
#[cfg(feature = "alloc")]
fn test_peek_as_str_owned_string() {
let s = alloc::string::String::from("owned string");
let peek = Peek::new(&s);
assert_eq!(peek.as_str(), Some("owned string"));
}
#[test]
fn test_peek_as_str_double_reference() {
let value = &"hello";
let peek = Peek::new(&value);
assert_eq!(peek.as_str(), Some("hello"));
}
#[test]
fn test_covariant_peek_from_covariant_type() {
let value = 42i32;
let peek = Peek::new(&value);
let covariant = CovariantPeek::new(peek);
assert!(covariant.is_some());
let covariant = covariant.unwrap();
assert_eq!(covariant.shape(), peek.shape());
}
#[test]
fn test_covariant_peek_from_ref() {
let value = 42i32;
let covariant = CovariantPeek::from_ref(&value);
assert!(covariant.is_some());
}
#[test]
fn test_covariant_peek_deref_to_peek() {
let value = "hello";
let peek = Peek::new(&value);
let covariant = CovariantPeek::new(peek).unwrap();
assert_eq!(covariant.as_str(), Some("hello"));
assert_eq!(covariant.shape(), peek.shape());
}
#[test]
fn test_covariant_peek_into_peek() {
let value = 42i32;
let original_peek = Peek::new(&value);
let covariant = CovariantPeek::new(original_peek).unwrap();
let recovered_peek = covariant.into_peek();
assert_eq!(recovered_peek.shape(), original_peek.shape());
}
#[test]
fn test_covariant_peek_lifetime_covariance() {
fn use_shorter<'a>(_p: CovariantPeek<'_, 'a>) {}
let value = 42i32;
let covariant: CovariantPeek<'_, 'static> = CovariantPeek::from_ref(&value).unwrap();
use_shorter(covariant);
}
#[test]
#[cfg(feature = "alloc")]
fn test_covariant_peek_vec_type() {
let vec = alloc::vec![1i32, 2, 3];
let peek = Peek::new(&vec);
let covariant = CovariantPeek::new(peek);
assert!(covariant.is_some());
}
#[test]
#[cfg(feature = "alloc")]
fn test_covariant_peek_option_type() {
let opt = Some(42i32);
let peek = Peek::new(&opt);
let covariant = CovariantPeek::new(peek);
assert!(covariant.is_some());
}
#[derive(Debug, Clone, facet::Facet)]
#[facet(metadata_container)]
struct Spanned<T> {
value: T,
#[facet(metadata = "span")]
span: Option<crate::Span>,
}
impl<T> Spanned<T> {
fn new(value: T, span: crate::Span) -> Self {
Self {
value,
span: Some(span),
}
}
}
#[test]
fn test_spanned_structural_hash_ignores_span() {
use crate::Span;
use core::hash::Hasher;
use std::hash::DefaultHasher;
let a = Spanned::new(42i32, Span::new(0, 10));
let b = Spanned::new(42i32, Span::new(100, 20));
let mut hasher_a = DefaultHasher::new();
Peek::new(&a).structural_hash(&mut hasher_a);
let hash_a = hasher_a.finish();
let mut hasher_b = DefaultHasher::new();
Peek::new(&b).structural_hash(&mut hasher_b);
let hash_b = hasher_b.finish();
assert_eq!(
hash_a, hash_b,
"Spanned values with same inner value should have same structural hash"
);
}
#[test]
fn test_spanned_structural_hash_differs_for_different_values() {
use crate::Span;
use core::hash::Hasher;
use std::hash::DefaultHasher;
let a = Spanned::new(42i32, Span::new(0, 10));
let b = Spanned::new(99i32, Span::new(0, 10));
let mut hasher_a = DefaultHasher::new();
Peek::new(&a).structural_hash(&mut hasher_a);
let hash_a = hasher_a.finish();
let mut hasher_b = DefaultHasher::new();
Peek::new(&b).structural_hash(&mut hasher_b);
let hash_b = hasher_b.finish();
assert_ne!(
hash_a, hash_b,
"Spanned values with different inner values should have different structural hashes"
);
}
#[test]
fn test_spanned_field_metadata() {
use facet_core::{Type, UserType};
let shape = <Spanned<i32> as facet_core::Facet>::SHAPE;
let struct_type = match shape.ty {
Type::User(UserType::Struct(st)) => st,
_ => panic!("Expected struct type"),
};
let span_field = struct_type
.fields
.iter()
.find(|f| f.name == "span")
.expect("Should have span field");
assert!(
span_field.is_metadata(),
"span field should be marked as metadata"
);
assert_eq!(
span_field.metadata_kind(),
Some("span"),
"span field should have metadata kind 'span'"
);
let value_field = struct_type
.fields
.iter()
.find(|f| f.name == "value")
.expect("Should have value field");
assert!(
!value_field.is_metadata(),
"value field should not be marked as metadata"
);
}
}