use std::marker::PhantomData;
use std::cmp::{Eq, PartialEq, Ord, PartialOrd, Ordering};
pub struct FieldRef<T, U> {
offset: usize,
phantom: PhantomData<(T, U)>,
}
impl<T, U> FieldRef<T, U> {
pub unsafe fn from_offset(offset: usize) -> Self {
Self { offset, phantom: PhantomData }
}
pub unsafe fn from_pointers(obj: *const T, field: *const U) -> Self {
Self::from_offset(field as usize - obj as usize)
}
pub unsafe fn from_references(obj: &T, field: &U) -> Self {
Self::from_pointers(obj, field)
}
pub fn offset(&self) -> usize {
self.offset
}
pub fn get<'a, 'b>(&'a self, obj: &'b T) -> &'b U {
let addr = obj as *const _ as usize + self.offset;
unsafe { &*(addr as *const U) }
}
#[deprecated(note = "Use `get` instead")]
pub fn get_ref<'a, 'b>(&'a self, obj: &'b T) -> &'b U {
self.get(obj)
}
pub fn get_mut<'a, 'b>(&'a self, obj: &'b mut T) -> &'b mut U {
let addr = obj as *mut _ as usize + self.offset;
unsafe { &mut *(addr as *mut U) }
}
pub fn chain<V>(&self, fr: FieldRef<U, V>) -> FieldRef<T, V> {
unsafe { FieldRef::<T, V>::from_offset(self.offset + fr.offset) }
}
pub fn as_opt_field_ref(&self) -> FieldRefAsOptionFieldRef<T, U> {
FieldRefAsOptionFieldRef(*self)
}
}
impl<T, U> Clone for FieldRef<T, U> {
fn clone(&self) -> Self {
Self { offset: self.offset, phantom: PhantomData }
}
}
impl<T, U> Copy for FieldRef<T, U> {}
impl<T, U> std::fmt::Debug for FieldRef<T, U> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
write!(f, "FieldRef {{ offset: {} }}", self.offset)
}
}
impl<T, U> PartialEq for FieldRef<T, U> {
fn eq(&self, other: &Self) -> bool {
self.offset == other.offset
}
}
impl<T, U> Eq for FieldRef<T, U> {}
impl<T, U> PartialOrd for FieldRef<T, U> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.offset.partial_cmp(&other.offset)
}
}
impl<T, U> Ord for FieldRef<T, U> {
fn cmp(&self, other: &Self) -> Ordering {
self.offset.cmp(&other.offset)
}
}
pub trait OptionFieldRef<'x> where Self: Copy {
type Input;
type Output;
fn get<'a>(&'a self, obj: &'x Self::Input) -> Option<&'x Self::Output>;
fn get_mut<'a>(&'a self, obj: &'x mut Self::Input) -> Option<&'x mut Self::Output>;
fn chain<FR, R: 'x>(&self, fr: FR) -> OptionFieldRefChain<Self, FR>
where
FR: OptionFieldRef<'x, Input = Self::Output, Output = R> + Copy
{
OptionFieldRefChain(*self, fr)
}
}
pub struct EnumFieldRef<E, T> {
extractor: fn(&E) -> Option<&T>,
mut_extractor: fn(&mut E) -> Option<&mut T>,
}
impl<E, T> EnumFieldRef<E, T> {
pub fn new(extractor: fn(&E) -> Option<&T>, mut_extractor: fn(&mut E) -> Option<&mut T>) -> Self {
Self { extractor, mut_extractor }
}
}
impl<'x, E: 'x, T: 'x> OptionFieldRef<'x> for EnumFieldRef<E, T> {
type Input = E;
type Output = T;
fn get<'a>(&'a self, e: &'x Self::Input) -> Option<&'x Self::Output> {
(self.extractor)(e)
}
fn get_mut<'a>(&'a self, e: &'x mut Self::Input) -> Option<&'x mut Self::Output> {
(self.mut_extractor)(e)
}
}
impl<E, T> Clone for EnumFieldRef<E, T> {
fn clone(&self) -> Self {
Self { extractor: self.extractor, mut_extractor: self.mut_extractor }
}
}
impl<E, T> Copy for EnumFieldRef<E, T> {}
pub struct OptionFieldRefChain<FR1: Copy, FR2: Copy>(FR1, FR2);
impl<'x, T: 'x, U: 'x, V: 'x, FR1, FR2> OptionFieldRef<'x> for OptionFieldRefChain<FR1, FR2> where
FR1: OptionFieldRef<'x, Input = T, Output = U>,
FR2: OptionFieldRef<'x, Input = U, Output = V>,
{
type Input = T;
type Output = V;
fn get<'a>(&'a self, obj: &'x Self::Input) -> Option<&'x Self::Output> {
self.0.get(obj).and_then(|y| self.1.get(y))
}
fn get_mut<'a>(&'a self, obj: &'x mut Self::Input) -> Option<&'x mut Self::Output> {
self.0.get_mut(obj).and_then(|y| self.1.get_mut(y))
}
}
impl <FR1: Copy, FR2: Copy> Clone for OptionFieldRefChain<FR1, FR2> {
fn clone(&self) -> Self {
OptionFieldRefChain(self.0, self.1)
}
}
impl <FR1: Copy, FR2: Copy> Copy for OptionFieldRefChain<FR1, FR2> {}
pub struct FieldRefAsOptionFieldRef<T, U>(FieldRef<T, U>);
impl<'x, T, U> OptionFieldRef<'x> for FieldRefAsOptionFieldRef<T, U> {
type Input = T;
type Output = U;
fn get<'a>(&'a self, obj: &'x Self::Input) -> Option<&'x Self::Output> {
Some(self.0.get(obj))
}
fn get_mut<'a>(&'a self, obj: &'x mut Self::Input) -> Option<&'x mut Self::Output> {
Some(self.0.get_mut(obj))
}
}
impl<T, U> Clone for FieldRefAsOptionFieldRef<T, U> {
fn clone(&self) -> Self {
FieldRefAsOptionFieldRef(self.0)
}
}
impl<T, U> Copy for FieldRefAsOptionFieldRef<T, U> {}
pub trait GetField where Self: Sized {
fn get_field<T>(&self, fr: FieldRef<Self, T>) -> &T;
fn try_get_field<'x, FR, T>(&'x self, fr: FR) -> Option<&'x T>
where
FR: OptionFieldRef<'x, Input = Self, Output = T>;
}
#[macro_export]
macro_rules! field_ref_of {
($t:ty $(=> $f:tt)+) => {
unsafe {
let base = ::std::ptr::null() as *const $t;
$crate::FieldRef::from_pointers(base, &(*base)$(.$f)+)
}
};
}
pub trait GetFieldMut where Self: Sized {
fn get_field_mut<T>(&mut self, fr: FieldRef<Self, T>) -> &mut T;
fn try_get_field_mut<'x, FR, T>(&'x mut self, fr: FR) -> Option<&'x mut T>
where
FR: OptionFieldRef<'x, Input = Self, Output = T>;
}
impl<S: Sized> GetField for S {
fn get_field<T>(&self, fr: FieldRef<Self, T>) -> &T {
fr.get(self)
}
fn try_get_field<'x, FR, T>(&'x self, fr: FR) -> Option<&'x T>
where
FR: OptionFieldRef<'x, Input = Self, Output = T>
{
fr.get(self)
}
}
impl<S: Sized> GetFieldMut for S {
fn get_field_mut<T>(&mut self, fr: FieldRef<Self, T>) -> &mut T {
fr.get_mut(self)
}
fn try_get_field_mut<'x, FR, T>(&'x mut self, fr: FR) -> Option<&'x mut T>
where
FR: OptionFieldRef<'x, Input = Self, Output = T>
{
fr.get_mut(self)
}
}
#[macro_export]
macro_rules! opt_field_ref_of {
($e:path { $f:tt } $(=> $fs:tt)*) => {
$crate::EnumFieldRef::new(
$crate::enum_field_extractor!($e => $f $(=> $fs)*),
$crate::enum_field_extractor!($e => $f $(=> $fs)*, mut)
)
};
($e:path { $f:tt } $(=> $fs:tt)* & $($tts:tt)+) => {
$crate::EnumFieldRef::new(
$crate::enum_field_extractor!($e => $f $(=> $fs)*),
$crate::enum_field_extractor!($e => $f $(=> $fs)*, mut)
)
.chain($crate::opt_field_ref_of!($($tts)+))
};
($t:ty $(=> $f:tt)+) => {
$crate::field_ref_of!($t $(=> $f)+).as_opt_field_ref()
};
($t:ty $(=> $f:tt)+ & $($tts:tt)+) => {
$crate::field_ref_of!($t $(=> $f)+).as_opt_field_ref()
.chain($crate::opt_field_ref_of!($($tts)+))
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! enum_field_extractor {
($e:path => $f:tt $(=> $fs:tt)* $(, $mut:tt)*) => {
#[allow(non_shorthand_field_patterns)]
|e| if let &$($mut)* $e { $f: ref $($mut)* x, .. } = e {
Some(&$($mut)*(*x) $(.$fs)*)
} else {
None
}
};
}
#[cfg(test)]
mod tests {
use super::*;
#[repr(C)] struct Foo(u32, u32);
struct Bar {
foo: Foo,
x: u32,
}
#[test]
fn basic_test() {
let mut foo = Foo(10, 20);
let fr1 = field_ref_of!(Foo => 0);
let fr2 = field_ref_of!(Foo => 1);
assert_eq!(fr1.get(&foo), &10);
assert_eq!(foo.get_field(fr2), &20);
*fr2.get_mut(&mut foo) = 30;
*foo.get_field_mut(fr1) = 40;
assert_eq!(foo.0, 40);
assert_eq!(foo.1, 30);
}
#[test]
fn multi_level_test() {
let bar = Bar{ foo: Foo(10, 20), x: 30 };
let fr1 = field_ref_of!(Bar => foo => 1);
let fr2 = field_ref_of!(Bar => foo);
let fr3 = field_ref_of!(Foo => 1);
let fr4 = field_ref_of!(Bar => x);
assert_eq!(bar.get_field(fr1), &20);
assert_eq!(bar.get_field(fr2.chain(fr3)), &20);
assert_eq!(bar.get_field(fr4), &30);
}
#[test]
fn debug_test() {
let fr = unsafe { FieldRef::<Foo, u32>::from_offset(100) };
assert_eq!(format!("{:?}", fr), "FieldRef { offset: 100 }");
}
#[test]
fn eq_test() {
let fr1 = field_ref_of!(Bar => foo => 1);
let fr2 = field_ref_of!(Bar => foo);
let fr3 = field_ref_of!(Foo => 1);
let fr4 = field_ref_of!(Bar => x);
assert!(fr1 != fr4);
assert!(fr1 == fr2.chain(fr3));
}
#[test]
fn ord_test() {
let fr1 = field_ref_of!(Bar => foo => 0);
let fr2 = field_ref_of!(Bar => foo => 1);
let fr3 = field_ref_of!(Bar => foo);
let fr4 = field_ref_of!(Foo => 1);
assert_eq!(fr1.cmp(&fr2), Ordering::Less);
assert_eq!(fr2.cmp(&fr1), Ordering::Greater);
assert_eq!(fr2.cmp(&fr3.chain(fr4)), Ordering::Equal);
}
#[derive(Debug, Eq, PartialEq)]
enum E {
A(u32, u32),
B{ x: u32, y: u32 },
C,
}
mod sub {
#[allow(dead_code)]
pub enum E2 {
X(u32),
Y,
}
}
#[test]
fn enum_field_basic_test() {
let fr1 = opt_field_ref_of!(E::A{1});
let fr2 = opt_field_ref_of!(E::B{x});
let mut e1 = E::A(10, 20);
let e2 = E::B{ x: 30, y: 40 };
let e3 = E::C;
assert_eq!(fr1.get(&e1), Some(&20));
assert_eq!(fr1.get(&e2), None);
assert_eq!(fr1.get(&e3), None);
*fr1.get_mut(&mut e1).unwrap() = 100;
assert_eq!(e1, E::A(10, 100));
assert_eq!(fr2.get(&e2), Some(&30));
}
#[test]
fn enum_field_enum_with_path_test() {
let fr1 = opt_field_ref_of!(sub::E2::X{0});
let e1 = sub::E2::X(10);
assert_eq!(fr1.get(&e1), Some(&10));
}
}