structural-typing 0.1.4

Type-level field presence tracking for Rust structs, inspired by TypeScript
Documentation
//! Extract selected fields from structs, returning the selected fields and remainder.
//!
//! Use `extract()` when extraction always succeeds (e.g., `Present` → `Optional`).
//! Use `try_extract()` when extraction may fail (e.g., `Optional` → `Present` can fail if `None`).

use core::marker::PhantomData;

use crate::access::Access;

/// Trait for compile-time checked extraction from one access type to another.
///
/// Returns both the extracted value (in target access type) and the remainder.
pub trait Extract<A: Access<T>, T>: Access<T> + Sized {
    /// Extract a value into target type and remainder parts.
    fn extract(self) -> (A, <A as Access<T>>::RemainderFrom<Self>);
}

/// Trait for runtime checked extraction that may fail.
///
/// Returns Ok with (extracted, remainder) on success, or Err with original value on failure.
///
/// Most conversions delegate to `Extract` and always succeed; only `Option<T>` → `T` can fail when the value is `None`.
pub trait TryExtract<A: Access<T>, T>: Access<T> + Sized {
    /// Try to extract a value into target type and remainder parts.
    ///
    /// # Errors
    ///
    /// Returns `Err` with the original value if the extraction cannot be performed
    /// (e.g., when trying to convert `Option<T>` to `T` but the value is `None`).
    fn try_extract(self) -> Result<(A, <A as Access<T>>::RemainderFrom<Self>), Self>;
}

impl<T, A: Access<T>> Extract<PhantomData<T>, T> for A {
    fn extract(
        self,
    ) -> (
        PhantomData<T>,
        <PhantomData<T> as Access<T>>::RemainderFrom<Self>,
    ) {
        (PhantomData, self)
    }
}

impl<T, A: Access<T>> Extract<Option<T>, T> for A {
    fn extract(self) -> (Option<T>, <Option<T> as Access<T>>::RemainderFrom<Self>) {
        (self.into_option(), PhantomData)
    }
}

impl<T> Extract<T, T> for T {
    fn extract(self) -> (T, <T as Access<T>>::RemainderFrom<Self>) {
        (self, PhantomData)
    }
}

impl<T, A: Access<T>> TryExtract<PhantomData<T>, T> for A {
    fn try_extract(
        self,
    ) -> Result<
        (
            PhantomData<T>,
            <PhantomData<T> as Access<T>>::RemainderFrom<Self>,
        ),
        Self,
    >
    where
        PhantomData<T>: Access<T>,
    {
        Ok(self.extract())
    }
}

impl<T, A: Access<T>> TryExtract<Option<T>, T> for A {
    fn try_extract(self) -> Result<(Option<T>, <Option<T> as Access<T>>::RemainderFrom<Self>), Self>
    where
        Option<T>: Access<T>,
    {
        Ok(self.extract())
    }
}

impl<T> TryExtract<T, T> for T {
    fn try_extract(self) -> Result<(T, <T as Access<T>>::RemainderFrom<Self>), Self> {
        Ok(self.extract())
    }
}

impl<T> TryExtract<T, T> for Option<T> {
    fn try_extract(self) -> Result<(T, <T as Access<T>>::RemainderFrom<Self>), Self> {
        match self {
            Some(v) => Ok((v, PhantomData)),
            None => Err(None),
        }
    }
}