#![doc = r#"Pathmod Core — runtime types for composable field accessors
This crate provides the runtime `Accessor<T, F>` type used by the derive macros in
`pathmod_derive` and re-exported by the `pathmod` crate. An `Accessor<T, F>` is a
small, `Copy` value that focuses into a field `F` within a root `T`.
Highlights
- Zero-cost composition: `Accessor` stores a byte offset from `&T` to `&F`; composing
accessors is O(1) offset addition.
- Safe surface: All public APIs are safe. Internals use pointer arithmetic; constructors
compute valid offsets for you.
- Clear clone semantics (MVP): `set_clone` only requires `F: Clone` and does not require
`T: Clone`, even when composed deeply.
Quick example
```rust
use pathmod_core::Accessor;
#[derive(Debug, PartialEq)]
struct Bar { x: i32 }
#[derive(Debug, PartialEq)]
struct Foo { a: i32, b: Bar }
fn acc_b() -> Accessor<Foo, Bar> {
fn get_ref(f: &Foo) -> &Bar { &f.b }
fn get_mut(f: &mut Foo) -> &mut Bar { &mut f.b }
Accessor::from_fns(get_ref, get_mut)
}
fn acc_x() -> Accessor<Bar, i32> {
fn get_ref(b: &Bar) -> &i32 { &b.x }
fn get_mut(b: &mut Bar) -> &mut i32 { &mut b.x }
Accessor::from_fns(get_ref, get_mut)
}
let mut foo = Foo { a: 1, b: Bar { x: 2 } };
let acc = acc_b().compose(acc_x());
acc.set_mut(&mut foo, |v| *v += 5);
assert_eq!(foo.b.x, 7);
```
Safety notes
- Internally, accessors are represented by a byte offset and use unsafe pointer arithmetic
to project fields. The public API is safe when accessors are constructed by the provided
derive macros or `from_fns`.
"#]
use core::marker::PhantomData;
#[derive(Debug, Clone, Copy)]
pub struct Accessor<T, F> {
offset: isize,
_phantom: PhantomData<fn(T) -> F>,
}
impl<T, F> Accessor<T, F> {
pub const unsafe fn from_offset(offset: isize) -> Self {
Self {
offset,
_phantom: PhantomData,
}
}
pub fn from_fns(get_ref: fn(&T) -> &F, _get_mut: fn(&mut T) -> &mut F) -> Self {
let base = core::ptr::null::<T>();
unsafe fn to_raw<T, F>(f: fn(&T) -> &F) -> fn(*const T) -> *const F {
core::mem::transmute::<fn(&T) -> &F, fn(*const T) -> *const F>(f)
}
let raw_get: fn(*const T) -> *const F = unsafe { to_raw(get_ref) };
let field_ptr = raw_get(base);
let offset = (field_ptr as isize) - (base as isize);
unsafe { Accessor::from_offset(offset) }
}
pub fn get<'a>(&self, root: &'a T) -> &'a F {
unsafe {
let base = root as *const T as *const u8;
let ptr = base.offset(self.offset) as *const F;
&*ptr
}
}
pub fn get_mut<'a>(&self, root: &'a mut T) -> &'a mut F {
unsafe {
let base = root as *mut T as *mut u8;
let ptr = base.offset(self.offset) as *mut F;
&mut *ptr
}
}
pub fn set(&self, root: &mut T, value: F) {
*self.get_mut(root) = value;
}
pub fn set_mut(&self, root: &mut T, f: impl FnOnce(&mut F)) {
f(self.get_mut(root));
}
pub fn set_clone(&self, root: &mut T, value: &F)
where
F: Clone,
{
*self.get_mut(root) = value.clone();
}
pub fn compose<V>(self, next: Accessor<F, V>) -> Accessor<T, V> {
let offset = self.offset + next.offset;
unsafe { Accessor::from_offset(offset) }
}
}
pub trait Indexing<T, E> {
fn get_at<'a>(&self, root: &'a T, idx: usize) -> &'a E;
fn get_mut_at<'a>(&self, root: &'a mut T, idx: usize) -> &'a mut E;
fn set_at(&self, root: &mut T, idx: usize, value: E);
fn set_mut_at(&self, root: &mut T, idx: usize, f: impl FnOnce(&mut E));
fn set_clone_at(&self, root: &mut T, idx: usize, value: &E)
where
E: Clone;
}
impl<T, E> Indexing<T, E> for Accessor<T, Vec<E>> {
fn get_at<'a>(&self, root: &'a T, idx: usize) -> &'a E {
&self.get(root)[idx]
}
fn get_mut_at<'a>(&self, root: &'a mut T, idx: usize) -> &'a mut E {
&mut self.get_mut(root)[idx]
}
fn set_at(&self, root: &mut T, idx: usize, value: E) {
self.get_mut(root)[idx] = value;
}
fn set_mut_at(&self, root: &mut T, idx: usize, f: impl FnOnce(&mut E)) {
f(&mut self.get_mut(root)[idx]);
}
fn set_clone_at(&self, root: &mut T, idx: usize, value: &E)
where
E: Clone,
{
self.get_mut(root)[idx] = value.clone();
}
}
pub mod prelude {
pub use crate::Accessor;
pub use crate::Indexing;
}