1
0
Fork 0

Move PersistentField and helpers to dedicated mod

As mentioned in the previous commit's breaking changes entry.
This commit is contained in:
Robbert van der Helm 2022-10-20 12:11:04 +02:00
parent fb71d0fcce
commit 2a0ed0c5ec
7 changed files with 143 additions and 140 deletions

View file

@ -354,9 +354,9 @@ pub fn derive_params(input: TokenStream) -> TokenStream {
.map(|PersistentField { field, key }| { .map(|PersistentField { field, key }| {
( (
quote! { quote! {
match ::nih_plug::param::internals::PersistentField::map( match ::nih_plug::param::persist::PersistentField::map(
&self.#field, &self.#field,
::nih_plug::param::internals::serialize_field, ::nih_plug::param::persist::serialize_field,
) { ) {
Ok(data) => { Ok(data) => {
serialized.insert(String::from(#key), data); serialized.insert(String::from(#key), data);
@ -372,9 +372,9 @@ pub fn derive_params(input: TokenStream) -> TokenStream {
}, },
quote! { quote! {
#key => { #key => {
match ::nih_plug::param::internals::deserialize_field(&data) { match ::nih_plug::param::persist::deserialize_field(&data) {
Ok(deserialized) => { Ok(deserialized) => {
::nih_plug::param::internals::PersistentField::set( ::nih_plug::param::persist::PersistentField::set(
&self.#field, &self.#field,
deserialized, deserialized,
); );

View file

@ -10,7 +10,7 @@ use baseview::{Size, WindowHandle, WindowOpenOptions, WindowScalePolicy};
use crossbeam::atomic::AtomicCell; use crossbeam::atomic::AtomicCell;
use egui::Context; use egui::Context;
use egui_baseview::EguiWindow; use egui_baseview::EguiWindow;
use nih_plug::param::internals::PersistentField; use nih_plug::param::persist::PersistentField;
use nih_plug::prelude::{Editor, GuiContext, ParamSetter, ParentWindowHandle}; use nih_plug::prelude::{Editor, GuiContext, ParamSetter, ParentWindowHandle};
use parking_lot::RwLock; use parking_lot::RwLock;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -63,7 +63,7 @@ where
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct EguiState { pub struct EguiState {
/// The window's size in logical pixels before applying `scale_factor`. /// The window's size in logical pixels before applying `scale_factor`.
#[serde(with = "nih_plug::param::internals::serialize_atomic_cell")] #[serde(with = "nih_plug::param::persist::serialize_atomic_cell")]
size: AtomicCell<(u32, u32)>, size: AtomicCell<(u32, u32)>,
/// Whether the editor's window is currently open. /// Whether the editor's window is currently open.
#[serde(skip)] #[serde(skip)]

View file

@ -92,7 +92,7 @@
use baseview::{WindowOpenOptions, WindowScalePolicy}; use baseview::{WindowOpenOptions, WindowScalePolicy};
use crossbeam::atomic::AtomicCell; use crossbeam::atomic::AtomicCell;
use crossbeam::channel; use crossbeam::channel;
use nih_plug::param::internals::PersistentField; use nih_plug::param::persist::PersistentField;
use nih_plug::prelude::{Editor, GuiContext, ParentWindowHandle}; use nih_plug::prelude::{Editor, GuiContext, ParentWindowHandle};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt::Debug; use std::fmt::Debug;

View file

@ -77,11 +77,11 @@ where
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct ViziaState { pub struct ViziaState {
/// The window's size in logical pixels before applying `scale_factor`. /// The window's size in logical pixels before applying `scale_factor`.
#[serde(with = "nih_plug::param::internals::serialize_atomic_cell")] #[serde(with = "nih_plug::param::persist::serialize_atomic_cell")]
size: AtomicCell<(u32, u32)>, size: AtomicCell<(u32, u32)>,
/// A scale factor that should be applied to `size` separate from from any system HiDPI scaling. /// A scale factor that should be applied to `size` separate from from any system HiDPI scaling.
/// This can be used to allow GUIs to be scaled uniformly. /// This can be used to allow GUIs to be scaled uniformly.
#[serde(with = "nih_plug::param::internals::serialize_atomic_cell")] #[serde(with = "nih_plug::param::persist::serialize_atomic_cell")]
scale_factor: AtomicCell<f64>, scale_factor: AtomicCell<f64>,
/// Whether the editor's window is currently open. /// Whether the editor's window is currently open.
#[serde(skip)] #[serde(skip)]

View file

@ -20,6 +20,7 @@ mod float;
mod integer; mod integer;
pub mod internals; pub mod internals;
pub mod persist;
pub mod range; pub mod range;
pub mod smoothing; pub mod smoothing;

View file

@ -2,34 +2,6 @@
use super::{Param, ParamFlags, ParamMut}; use super::{Param, ParamFlags, ParamMut};
/// Re-export for use in the [`Params`] proc-macro.
pub use serde_json::from_str as deserialize_field;
/// Re-export for use in the [`Params`] proc-macro.
pub use serde_json::to_string as serialize_field;
/// Can be used with the `#[serde(with = "nih_plug::param::internals::serialize_atomic_cell")]`
/// attribute to serialize `AtomicCell<T>`s.
pub mod serialize_atomic_cell {
use crossbeam::atomic::AtomicCell;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
pub fn serialize<S, T>(cell: &AtomicCell<T>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
T: Serialize + Copy,
{
cell.load().serialize(serializer)
}
pub fn deserialize<'de, D, T>(deserializer: D) -> Result<AtomicCell<T>, D::Error>
where
D: Deserializer<'de>,
T: Deserialize<'de> + Copy,
{
T::deserialize(deserializer).map(AtomicCell::new)
}
}
/// Internal pointers to parameters. This is an implementation detail used by the wrappers for type /// Internal pointers to parameters. This is an implementation detail used by the wrappers for type
/// erasure. /// erasure.
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
@ -48,27 +20,6 @@ pub enum ParamPtr {
unsafe impl Send for ParamPtr {} unsafe impl Send for ParamPtr {}
unsafe impl Sync for ParamPtr {} unsafe impl Sync for ParamPtr {}
/// Handles the functionality needed for persisting a non-parameter fields in a plugin's state.
/// These types can be used with [`Params`]' `#[persist = "..."]` attributes.
///
/// This should be implemented for some type with interior mutability containing a `T`.
//
// TODO: Modifying these fields (or any parameter for that matter) should mark the plugin's state
// as dirty.
pub trait PersistentField<'a, T>: Send + Sync
where
T: serde::Serialize + serde::Deserialize<'a>,
{
/// Update the stored `T` value using interior mutability.
fn set(&self, new_value: T);
/// Get a reference to the stored `T` value, and apply a function to it. This is used to
/// serialize the `T` value.
fn map<F, R>(&self, f: F) -> R
where
F: Fn(&T) -> R;
}
/// Generate a [`ParamPtr`] function that forwards the function call to the underlying `Param`. We /// Generate a [`ParamPtr`] function that forwards the function call to the underlying `Param`. We
/// can't have an `.as_param()` function since the return type would differ depending on the /// can't have an `.as_param()` function since the return type would differ depending on the
/// underlying parameter type, so instead we need to type erase all of the functions individually. /// underlying parameter type, so instead we need to type erase all of the functions individually.
@ -217,85 +168,3 @@ impl ParamPtr {
} }
} }
} }
impl<'a, T> PersistentField<'a, T> for std::sync::RwLock<T>
where
T: serde::Serialize + serde::Deserialize<'a> + Send + Sync,
{
fn set(&self, new_value: T) {
*self.write().expect("Poisoned RwLock on write") = new_value;
}
fn map<F, R>(&self, f: F) -> R
where
F: Fn(&T) -> R,
{
f(&self.read().expect("Poisoned RwLock on read"))
}
}
impl<'a, T> PersistentField<'a, T> for parking_lot::RwLock<T>
where
T: serde::Serialize + serde::Deserialize<'a> + Send + Sync,
{
fn set(&self, new_value: T) {
*self.write() = new_value;
}
fn map<F, R>(&self, f: F) -> R
where
F: Fn(&T) -> R,
{
f(&self.read())
}
}
impl<'a, T> PersistentField<'a, T> for std::sync::Mutex<T>
where
T: serde::Serialize + serde::Deserialize<'a> + Send + Sync,
{
fn set(&self, new_value: T) {
*self.lock().expect("Poisoned Mutex") = new_value;
}
fn map<F, R>(&self, f: F) -> R
where
F: Fn(&T) -> R,
{
f(&self.lock().expect("Poisoned Mutex"))
}
}
macro_rules! impl_persistent_field_parking_lot_mutex {
($ty:ty) => {
impl<'a, T> PersistentField<'a, T> for $ty
where
T: serde::Serialize + serde::Deserialize<'a> + Send + Sync,
{
fn set(&self, new_value: T) {
*self.lock() = new_value;
}
fn map<F, R>(&self, f: F) -> R
where
F: Fn(&T) -> R,
{
f(&self.lock())
}
}
};
}
impl<'a, T> PersistentField<'a, T> for atomic_refcell::AtomicRefCell<T>
where
T: serde::Serialize + serde::Deserialize<'a> + Send + Sync,
{
fn set(&self, new_value: T) {
*self.borrow_mut() = new_value;
}
fn map<F, R>(&self, f: F) -> R
where
F: Fn(&T) -> R,
{
f(&self.borrow())
}
}
impl_persistent_field_parking_lot_mutex!(parking_lot::Mutex<T>);
impl_persistent_field_parking_lot_mutex!(parking_lot::FairMutex<T>);

133
src/param/persist.rs Normal file
View file

@ -0,0 +1,133 @@
//! Traits and helpers for persistent fields. See the [`Params`][super::Params] trait for more
//! information.
/// Re-export for use in the [`Params`] proc-macro.
pub use serde_json::from_str as deserialize_field;
/// Re-export for use in the [`Params`] proc-macro.
pub use serde_json::to_string as serialize_field;
/// Handles the functionality needed for persisting a non-parameter fields in a plugin's state.
/// These types can be used with [`Params`]' `#[persist = "..."]` attributes.
///
/// This should be implemented for some type with interior mutability containing a `T`.
//
// TODO: Modifying these fields (or any parameter for that matter) should mark the plugin's state
// as dirty.
pub trait PersistentField<'a, T>: Send + Sync
where
T: serde::Serialize + serde::Deserialize<'a>,
{
/// Update the stored `T` value using interior mutability.
fn set(&self, new_value: T);
/// Get a reference to the stored `T` value, and apply a function to it. This is used to
/// serialize the `T` value.
fn map<F, R>(&self, f: F) -> R
where
F: Fn(&T) -> R;
}
impl<'a, T> PersistentField<'a, T> for std::sync::RwLock<T>
where
T: serde::Serialize + serde::Deserialize<'a> + Send + Sync,
{
fn set(&self, new_value: T) {
*self.write().expect("Poisoned RwLock on write") = new_value;
}
fn map<F, R>(&self, f: F) -> R
where
F: Fn(&T) -> R,
{
f(&self.read().expect("Poisoned RwLock on read"))
}
}
impl<'a, T> PersistentField<'a, T> for parking_lot::RwLock<T>
where
T: serde::Serialize + serde::Deserialize<'a> + Send + Sync,
{
fn set(&self, new_value: T) {
*self.write() = new_value;
}
fn map<F, R>(&self, f: F) -> R
where
F: Fn(&T) -> R,
{
f(&self.read())
}
}
impl<'a, T> PersistentField<'a, T> for std::sync::Mutex<T>
where
T: serde::Serialize + serde::Deserialize<'a> + Send + Sync,
{
fn set(&self, new_value: T) {
*self.lock().expect("Poisoned Mutex") = new_value;
}
fn map<F, R>(&self, f: F) -> R
where
F: Fn(&T) -> R,
{
f(&self.lock().expect("Poisoned Mutex"))
}
}
macro_rules! impl_persistent_field_parking_lot_mutex {
($ty:ty) => {
impl<'a, T> PersistentField<'a, T> for $ty
where
T: serde::Serialize + serde::Deserialize<'a> + Send + Sync,
{
fn set(&self, new_value: T) {
*self.lock() = new_value;
}
fn map<F, R>(&self, f: F) -> R
where
F: Fn(&T) -> R,
{
f(&self.lock())
}
}
};
}
impl<'a, T> PersistentField<'a, T> for atomic_refcell::AtomicRefCell<T>
where
T: serde::Serialize + serde::Deserialize<'a> + Send + Sync,
{
fn set(&self, new_value: T) {
*self.borrow_mut() = new_value;
}
fn map<F, R>(&self, f: F) -> R
where
F: Fn(&T) -> R,
{
f(&self.borrow())
}
}
impl_persistent_field_parking_lot_mutex!(parking_lot::Mutex<T>);
impl_persistent_field_parking_lot_mutex!(parking_lot::FairMutex<T>);
/// Can be used with the `#[serde(with = "nih_plug::param::internals::serialize_atomic_cell")]`
/// attribute to serialize `AtomicCell<T>`s.
pub mod serialize_atomic_cell {
use crossbeam::atomic::AtomicCell;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
pub fn serialize<S, T>(cell: &AtomicCell<T>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
T: Serialize + Copy,
{
cell.load().serialize(serializer)
}
pub fn deserialize<'de, D, T>(deserializer: D) -> Result<AtomicCell<T>, D::Error>
where
D: Deserializer<'de>,
T: Deserialize<'de> + Copy,
{
T::deserialize(deserializer).map(AtomicCell::new)
}
}