morphix 0.18.5

Observing and serializing mutations
Documentation
use std::ffi::{OsStr, OsString};
use std::path::{Path, PathBuf};
use std::ptr::NonNull;

use crate::Mutations;
use crate::general::{DebugHandler, GeneralHandler, GeneralObserver, SerializeHandler};
use crate::helper::macros::shallow_observer;
use crate::helper::{AsDeref, AsDerefMut, ObserverState, Unsigned};
use crate::impls::shallow::ShallowMut;
use crate::observe::{DefaultSpec, RefObserve};

shallow_observer! {
    struct PathObserver(Path);
}

impl<'ob, S: ?Sized, D> PathObserver<'ob, S, D>
where
    D: Unsigned,
    S: AsDerefMut<D, Target = Path>,
{
    /// See [`Path::as_mut_os_str`].
    pub fn as_mut_os_str(&mut self) -> ShallowMut<'_, OsStr> {
        ShallowMut::new((*self.ptr).as_deref_mut().as_mut_os_str(), &raw mut self.mutated)
    }
}

macro_rules! generic_impl_partial_eq {
    ($(impl $([$($gen:tt)*])? _ for $ty:ty);* $(;)?) => {
        $(
            impl<'ob, $($($gen)*,)? S: ?Sized, D> PartialEq<$ty> for PathObserver<'ob, S, D>
            where
                D: Unsigned,
                S: AsDeref<D>,
                S::Target: PartialEq<$ty>,
            {
                fn eq(&self, other: &$ty) -> bool {
                    (***self).as_deref().eq(other)
                }
            }
        )*
    };
}

generic_impl_partial_eq! {
    impl _ for str;
    impl _ for String;
    impl _ for OsStr;
    impl _ for OsString;
    impl _ for Path;
    impl _ for PathBuf;
    impl ['a, T] _ for &'a T;
    impl ['a, T: ToOwned] _ for std::borrow::Cow<'a, T>;
}

pub struct PathHandler {
    raw_parts: Option<Option<(NonNull<()>, usize)>>,
}

impl ObserverState for PathHandler {
    type Target = Path;

    fn invalidate(this: &mut Self, value: &Path) {
        this.raw_parts.get_or_insert_with(|| {
            value
                .to_str()
                .map(|str| (NonNull::from(str).cast::<()>(), str.chars().count()))
        });
    }
}

impl GeneralHandler for PathHandler {
    fn observe(_: &Path) -> Self {
        Self { raw_parts: None }
    }
}

impl SerializeHandler for PathHandler {
    unsafe fn flush(&mut self, value: &Path) -> Mutations {
        let (old_addr, old_len) = match self.raw_parts.take() {
            None => return Mutations::new(),
            Some(None) => return Mutations::replace(value),
            Some(Some(parts)) => parts,
        };
        let Some(str) = value.to_str() else {
            return Mutations::replace(value);
        };
        let new_addr = NonNull::from(str).cast::<()>();
        let new_len = str.chars().count();
        if new_addr != old_addr {
            return Mutations::replace(value);
        }
        if new_len < old_len {
            #[cfg(not(feature = "truncate"))]
            return Mutations::replace(value);
            #[cfg(feature = "truncate")]
            return Mutations::truncate(old_len - new_len);
        }
        if new_len > old_len {
            #[cfg(not(feature = "append"))]
            return Mutations::replace(value);
            #[cfg(feature = "append")]
            return Mutations::append(&str[old_len..]);
        }
        Mutations::new()
    }
}

impl DebugHandler for PathHandler {
    const NAME: &'static str = "PathHandler";
}

impl RefObserve for Path {
    type Observer<'ob, S, D>
        = GeneralObserver<'ob, PathHandler, S, D>
    where
        Self: 'ob,
        D: Unsigned,
        S: AsDeref<D, Target = Self> + ?Sized + 'ob;

    type Spec = DefaultSpec;
}