wnf 0.3.0

Safe bindings for the Windows Notification Facility
Documentation
//! Methods for updating state data while returning the previous value
//!
//! This module only adds inherent impls to [`OwnedState<T>`] and [`BorrowedState<'_, T>`](BorrowedState).

#![deny(unsafe_code)]

use std::io;

use crate::bytes::NoUninit;
use crate::read::Read;
use crate::state::{BorrowedState, OwnedState, RawState};

impl<T> OwnedState<T>
where
    T: Read<T> + NoUninit,
{
    /// Replaces the data of this state, returning the previous value
    ///
    /// This essentially queries the state data, updates it with the given value and returns the previous value.
    /// However, it tries to do so in a loop using change stamps to ensure that no concurrent update happens between
    /// querying and updating the data. Note that it does *not* reliably avoid concurrent updates while the actual
    /// update is happening. If another concurrent update makes the size of the state data exceed the internal
    /// capacity of the state (causing a reallocation), it may happen that this update does not have the desired
    /// effect on the state data.
    ///
    /// This produces an owned `T` on the stack and hence requires `T: Sized`. In order to produce a `Box<T>` for
    /// `T: ?Sized`, use the [`replace_boxed`](OwnedState::replace_boxed) method.
    ///
    /// For example, to set the value of a state to zero while returning the previous value:
    /// ```
    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
    /// use std::io;
    ///
    /// use wnf::{AsState, OwnedState};
    ///
    /// fn reset<S>(state: S) -> io::Result<u32>
    /// where
    ///     S: AsState<Data = u32>,
    /// {
    ///     state.as_state().replace(&0)
    /// }
    ///
    /// let state = OwnedState::create_temporary()?;
    /// state.set(&42)?;
    ///
    /// let previous_value = reset(&state)?;
    /// assert_eq!(previous_value, 42);
    /// # Ok(()) }
    /// ```
    ///
    /// # Errors
    /// Returns an error if querying or updating fails
    pub fn replace(&self, new_value: &T) -> io::Result<T> {
        self.raw.replace(new_value)
    }
}

impl<T> OwnedState<T>
where
    T: Read<Box<T>> + NoUninit + ?Sized,
{
    /// Replaces the data of this state, returning the previous value as a box
    ///
    /// This essentially queries the state data, updates it with the given value and returns the previous value.
    /// However, it tries to do so in a loop using change stamps to ensure that no concurrent update happens between
    /// querying and updating the data. Note that it does *not* reliably avoid concurrent updates while the actual
    /// update is happening. If another concurrent update makes the size of the state data exceed the internal
    /// capacity of the state (causing a reallocation), it may happen that this update does not have the desired
    /// effect on the state data.
    ///
    /// This produces a [`Box<T>`]. In order to produce an owned `T` on the stack (requiring `T: Sized`), use the
    /// [`replace`](OwnedState::replace) method.
    ///
    /// For example, to make a slice empty while returning the previous (boxed) slice:
    /// ```
    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
    /// use std::io;
    ///
    /// use wnf::{AsState, OwnedState};
    ///
    /// fn clear<S>(state: S) -> io::Result<Box<[u32]>>
    /// where
    ///     S: AsState<Data = [u32]>,
    /// {
    ///     state.as_state().replace_boxed(&[])
    /// }
    ///
    /// let state = OwnedState::<[u32]>::create_temporary()?;
    /// state.set(&[1, 2, 3])?;
    ///
    /// let previous_value = clear(&state)?;
    /// assert_eq!(*previous_value, [1, 2, 3]);
    /// # Ok(()) }
    /// ```
    ///
    /// # Errors
    /// Returns an error if querying or updating fails
    pub fn replace_boxed(&self, new_value: &T) -> io::Result<Box<T>> {
        self.raw.replace_boxed(new_value)
    }
}

impl<T> BorrowedState<'_, T>
where
    T: Read<T> + NoUninit,
{
    /// Replaces the data of this state, returning the previous value
    ///
    /// See [`OwnedState::replace`]
    pub fn replace(self, new_value: &T) -> io::Result<T> {
        self.raw.replace(new_value)
    }
}

impl<T> BorrowedState<'_, T>
where
    T: Read<Box<T>> + NoUninit + ?Sized,
{
    /// Replaces the data of this state, returning the previous value as a box
    ///
    /// See [`OwnedState::replace_boxed`]
    pub fn replace_boxed(self, new_value: &T) -> io::Result<Box<T>> {
        self.raw.replace_boxed(new_value)
    }
}

impl<T> RawState<T>
where
    T: Read<T> + NoUninit,
{
    /// Replaces the data of this state, returning the previous value
    fn replace(self, new_value: &T) -> io::Result<T> {
        self.replace_as(new_value)
    }
}

impl<T> RawState<T>
where
    T: Read<Box<T>> + NoUninit + ?Sized,
{
    /// Replaces the data of this state, returning the previous value as a box
    fn replace_boxed(self, new_value: &T) -> io::Result<Box<T>> {
        self.replace_as(new_value)
    }
}

impl<T> RawState<T>
where
    T: ?Sized,
{
    /// Replaces the data of this state, returning the previous value as a value of type `D`
    ///
    /// If `T: Sized`, then `D` can be either `T` or `Box<T>`.
    /// If `T: !Sized`, then `D` must be `Box<T>`.
    fn replace_as<D>(self, new_value: &T) -> io::Result<D>
    where
        T: Read<D> + NoUninit,
    {
        let mut old_value = None;

        self.apply_as(|value| {
            old_value = Some(value);
            new_value
        })?;

        Ok(old_value.unwrap())
    }
}