use core::any::TypeId;
use core::hash::{Hash, Hasher};
use core::marker::PhantomData;
#[expect(unused_imports)]
use crate::field;
#[derive(Debug)]
pub struct Field<S, T> {
field_path: &'static str,
_marker: PhantomData<fn() -> (S, T)>,
}
impl<S, T> Field<S, T> {
pub const PLACEHOLDER: Self = Self::new("$");
#[inline]
pub const fn new(field_path: &'static str) -> Self {
Self {
field_path,
_marker: PhantomData,
}
}
pub const fn field_path(&self) -> &'static str {
self.field_path
}
}
impl<S, T> Field<S, T>
where
S: 'static,
T: 'static,
{
#[inline]
pub const fn untyped(&self) -> UntypedField {
UntypedField::new::<S, T>(self.field_path)
}
}
impl<S, T> Hash for Field<S, T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.field_path.hash(state);
}
}
impl<S, T> PartialEq for Field<S, T> {
fn eq(&self, other: &Self) -> bool {
self.field_path == other.field_path
}
}
impl<S, T> Eq for Field<S, T> {}
impl<S, T> PartialOrd for Field<S, T> {
fn partial_cmp(
&self,
other: &Self,
) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<S, T> Ord for Field<S, T> {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.field_path.cmp(other.field_path)
}
}
impl<S, T> Clone for Field<S, T> {
#[inline]
fn clone(&self) -> Self {
*self
}
}
impl<S, T> Copy for Field<S, T> {}
pub struct _FieldBuilder<S, T> {
_field_marker: fn(source: S) -> T,
field_path: &'static str,
}
impl<S, T> _FieldBuilder<S, T> {
#[inline]
pub const fn new(
field_marker: fn(source: S) -> T,
field_path: &'static str,
) -> Self {
Self {
field_path,
_field_marker: field_marker,
}
}
#[inline]
pub const fn build(self) -> Field<S, T> {
Field {
field_path: self.field_path,
_marker: PhantomData,
}
}
}
#[macro_export]
macro_rules! field {
(<$source:ty>$(::$field:tt)*) => {
$crate::field::_FieldBuilder::new(
|source: $source| source$(.$field)*,
$crate::stringify_field!($(::$field)*),
)
.build()
};
}
#[derive(
Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord,
)]
pub struct UntypedField {
source_id: TypeId,
target_id: TypeId,
field_path: &'static str,
}
impl UntypedField {
#[inline]
pub const fn new<S, T>(field_path: &'static str) -> Self
where
S: 'static,
T: 'static,
{
Self {
source_id: TypeId::of::<S>(),
target_id: TypeId::of::<T>(),
field_path,
}
}
#[inline]
pub const fn placeholder() -> Self {
Self::placeholder_with_path("$")
}
#[inline]
pub const fn placeholder_with_path(
field_path: &'static str,
) -> Self {
Self::new::<(), ()>(field_path)
}
#[inline]
pub const fn source_id(&self) -> TypeId {
self.source_id
}
#[inline]
pub const fn target_id(&self) -> TypeId {
self.target_id
}
#[inline]
pub const fn field_path(&self) -> &'static str {
self.field_path
}
#[inline]
pub fn typed<S, T>(self) -> Option<Field<S, T>>
where
S: 'static,
T: 'static,
{
if self.source_id == TypeId::of::<S>()
&& self.target_id == TypeId::of::<T>()
{
return Some(self.typed_unchecked());
}
None
}
#[inline]
pub const fn typed_unchecked<S, T>(self) -> Field<S, T> {
Field::new(self.field_path)
}
}
impl<S, T> From<Field<S, T>> for UntypedField
where
S: 'static,
T: 'static,
{
#[inline]
fn from(field: Field<S, T>) -> Self {
field.untyped()
}
}
impl<S, T> From<&Field<S, T>> for UntypedField
where
S: 'static,
T: 'static,
{
#[inline]
fn from(field: &Field<S, T>) -> Self {
field.untyped()
}
}
#[macro_export]
macro_rules! stringify_field {
($(::$field:tt)*) => {
concat!($("::", stringify!($field),)*)
};
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(PartialEq, Debug, Clone)]
struct Foo(u32);
#[derive(PartialEq, Debug, Clone)]
struct NestedFoo {
inner: Foo,
}
#[test]
fn field_path_matches() {
const FIELD: Field<Foo, Foo> = field!(<Foo>);
assert_eq!(FIELD.field_path, "");
const FIELD_0: Field<Foo, u32> = field!(<Foo>::0);
assert_eq!(FIELD_0.field_path, stringify_field!(::0), "::0");
const FIELD_INNER_0: Field<NestedFoo, u32> =
field!(<NestedFoo>::inner::0);
assert_eq!(
FIELD_INNER_0.field_path,
stringify_field!(::inner::0),
"::inner::0"
);
}
}