blob: d054d1540a0f161bbadaa97f3df81dfbae9d31fc [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! Macros used with struct containing an immutable state and a RwLock to a mutable state.
//!
//! These macros define a new type of read and write guards that allow access to both the
//! mutable and immutable state. To use it, one must:
//! - Define the main struct (e.g. `Foo`) for the object with the immutable state.
//! - Define a struct for the mutable state (e.g. `FooMutableState`).
//! - Have a RwLock<> in the main struct for the mutable state (e.g. `mutable_state:
//! RwLock<FooMutableState>`).
//! - In the implementation of the main struct, add a call to the `state_accessor` macros:
//! ```
//! impl Foo {
//! state_accessor!(Foo, mutable_state);
//! }
//! ```
//! - Write the method on the guards using the state_implementation macro:
//! ```
//! state_implementation!(Foo, FooMutableState, {
//! // Some comment
//! fn do_something(&self) -> i32 {
//! 0
//! }
//! });
//! ```
//!
//! # Complete example:
//!
//! ```
//! pub struct FooMutableState {
//! y: i32,
//! }
//! pub struct Foo {
//! x: i32,
//! mutable_state: RwLock<FooMutableState>,
//! }
//! impl Foo {
//! fn new() -> Self {
//! Self {
//! x: 2,
//! mutable_state: RwLock::new(FooMutableState { y: 3 }),
//! }
//! }
//! state_accessor!(Foo, mutable_state);
//! }
//! state_implementation!(Foo, FooMutableState, {
//! // Some comment
//! fn x_and_y(&self) -> i32 {
//! self.base().x + self.y
//! }
//! /// Some rustdoc.
//! pub fn pub_x_and_y(&self) -> i32 {
//! self.x_and_y()
//! }
//! fn do_something(&self) {}
//! fn set_y(&mut self, other_y: i32) {
//! self.y = other_y;
//! }
//! pub fn pub_set_y(&mut self, other_y: i32) {
//! self.set_y(other_y)
//! }
//! fn do_something_mutable(&mut self) {
//! self.do_something();
//! }
//! pub fn with_lifecycle<'a>(&self, _n: &'a u32) {}
//! pub fn with_type<T>(&self, _n: &T) {}
//! pub fn with_lifecycle_and_type<'a, T>(&self, _n: &'a T) {}
//! pub fn with_lifecycle_on_self<'a, T>(&'a self, _n: &'a T) {}
//! });
//! ```
//!
//! # Generated code
//!
//! ```
//! pub struct FooMutableState {
//! y: i32,
//! }
//! pub struct Foo {
//! x: i32,
//! mutable_state: RwLock<FooMutableState>,
//! }
//! impl Foo {
//! fn new() -> Self {
//! Self {
//! x: 2,
//! mutable_state: RwLock::new(FooMutableState { y: 3 }),
//! }
//! }
//! #[allow(dead_code)]
//! pub fn read<'a>(self: &'a Arc<Foo>) -> FooReadGuardImpl<'a> {
//! FooReadGuardImpl::new(self, self.mutable_state.read())
//! }
//! #[allow(dead_code)]
//! pub fn write<'a>(self: &'a Arc<Foo>) -> FooWriteGuard<'a> {
//! FooWriteGuard::new(self, self.mutable_state.write())
//! }
//! }
//! type FooReadGuardImpl<'guard_lifetime> = ReadableState<'guard_lifetime, Foo, FooMutableState>;
//! pub type FooWriteGuard<'guard_lifetime> = WritableState<'guard_lifetime, Foo, FooMutableState>;
//! pub trait FooReadGuard<'guard_lifetime>:
//! Baseable<'guard_lifetime, Foo> + std::ops::Deref<Target = FooMutableState>
//! {
//! /// Some rustdoc.
//! fn pub_x_and_y(&self) -> i32;
//! fn with_lifecycle<'a>(&self, _n: &'a u32);
//! fn with_type<T>(&self, _n: &T);
//! fn with_lifecycle_and_type<'a, T>(&self, _n: &'a T);
//! pub fn with_lifecycle_on_self<'a, T>(&'a self, _n: &'a T) {}
//! }
//! impl<'guard_lifetime> FooReadGuard<'guard_lifetime> for FooReadGuardImpl<'guard_lifetime> {
//! /// Some rustdoc.
//! fn pub_x_and_y(&self) -> i32 {
//! self.x_and_y()
//! }
//! fn with_lifecycle<'a>(&self, _n: &'a u32) {}
//! fn with_type<T>(&self, _n: &T) {}
//! fn with_lifecycle_and_type<'a, T>(&self, _n: &'a T) {}
//! }
//! impl<'guard_lifetime> FooReadGuard<'guard_lifetime> for FooWriteGuard<'guard_lifetime> {
//! /// Some rustdoc.
//! fn pub_x_and_y(&self) -> i32 {
//! self.x_and_y()
//! }
//! fn with_lifecycle<'a>(&self, _n: &'a u32) {}
//! fn with_type<T>(&self, _n: &T) {}
//! fn with_lifecycle_and_type<'a, T>(&self, _n: &'a T) {}
//! pub fn with_lifecycle_on_self<'a, T>(&'a self, _n: &'a T) {}
//! }
//! impl<'guard_lifetime> FooReadGuardImpl<'guard_lifetime> {
//! fn x_and_y(&self) -> i32 {
//! self.base().x + self.y
//! }
//! fn do_something(&self) {}
//! }
//! impl<'guard_lifetime> FooWriteGuard<'guard_lifetime> {
//! fn x_and_y(&self) -> i32 {
//! self.base().x + self.y
//! }
//! fn do_something(&self) {}
//! fn set_y(&mut self, other_y: i32) {
//! self.y = other_y;
//! }
//! pub fn pub_set_y(&mut self, other_y: i32) {
//! self.set_y(other_y)
//! }
//! fn do_something_mutable(&mut self) {
//! self.do_something();
//! }
//! }
//! ```
use std::ops;
use std::sync::Arc;
use crate::lock::*;
/// Create the read() and write() accessor to respectively the read guard and write guard.
///
/// For a base struct named `Foo`, the read guard will be a trait named `FooReadGuard` and the
/// write guard a struct named `FooWriteGuard`.
macro_rules! state_accessor {
($base_name:ident, $field_name:ident) => {
paste::paste! {
#[allow(dead_code)]
pub fn read<'a>(self: &'a Arc<$base_name>) -> [<$base_name ReadGuardImpl>]<'a> {
[<$base_name ReadGuardImpl>]::new(self, self.$field_name.read())
}
#[allow(dead_code)]
pub fn write<'a>(self: &'a Arc<$base_name>) -> [<$base_name WriteGuard>]<'a> {
[<$base_name WriteGuard>]::new(self, self.$field_name.write())
}
}
};
}
/// Create the trait and struct for the read and write guards using the methods define inside the
/// macro.
macro_rules! state_implementation {
($base_name:ident, $mutable_name: ident, {
$(
$tt:tt
)*
}) => {
paste::paste! {
type [<$base_name ReadGuardImpl>]<'guard_lifetime> = ReadableState<'guard_lifetime, $base_name, $mutable_name>;
pub type [<$base_name WriteGuard>]<'guard_lifetime> = WritableState<'guard_lifetime, $base_name, $mutable_name>;
pub trait [<$base_name ReadGuard>]<'guard_lifetime>: Baseable<'guard_lifetime, $base_name> + std::ops::Deref<Target = $mutable_name>
{
filter_methods!(RoPublicMethodSignature, $($tt)*);
}
impl<'guard_lifetime> [<$base_name ReadGuard>]<'guard_lifetime> for [<$base_name ReadGuardImpl>]<'guard_lifetime> {
filter_methods!(RoPublicMethod, $($tt)*);
}
impl<'guard_lifetime> [<$base_name ReadGuard>]<'guard_lifetime> for [<$base_name WriteGuard>]<'guard_lifetime> {
filter_methods!(RoPublicMethod, $($tt)*);
}
impl<'guard_lifetime> [<$base_name ReadGuardImpl>]<'guard_lifetime> {
filter_methods!(RoPrivateMethod, $($tt)*);
}
impl<'guard_lifetime> [<$base_name WriteGuard>]<'guard_lifetime> {
filter_methods!(RoPrivateMethod, $($tt)*);
filter_methods!(RwMethod, $($tt)*);
}
}
};
}
pub trait Baseable<'a, B> {
fn base(&self) -> &'a Arc<B>;
}
pub struct ReadableState<'a, B, S> {
base: &'a Arc<B>,
state: RwLockReadGuard<'a, S>,
}
impl<'a, B, S> ReadableState<'a, B, S> {
pub fn new(base: &'a Arc<B>, state: RwLockReadGuard<'a, S>) -> Self {
Self { base, state }
}
}
impl<'a, B, S> Baseable<'a, B> for ReadableState<'a, B, S> {
fn base(&self) -> &'a Arc<B> {
self.base
}
}
impl<'a, B, S> ops::Deref for ReadableState<'a, B, S> {
type Target = S;
fn deref(&self) -> &Self::Target {
&self.state.deref()
}
}
pub struct WritableState<'a, B, S> {
base: &'a Arc<B>,
state: RwLockWriteGuard<'a, S>,
}
impl<'a, B, S> WritableState<'a, B, S> {
pub fn new(base: &'a Arc<B>, state: RwLockWriteGuard<'a, S>) -> Self {
Self { base, state }
}
}
impl<'a, B, S> Baseable<'a, B> for WritableState<'a, B, S> {
fn base(&self) -> &'a Arc<B> {
self.base
}
}
impl<'a, B, S> ops::Deref for WritableState<'a, B, S> {
type Target = S;
fn deref(&self) -> &Self::Target {
self.state.deref()
}
}
impl<'a, B, S> ops::DerefMut for WritableState<'a, B, S> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.state.deref_mut()
}
}
/// This macro matches the methods inside a `state_implementation!` macro depending on their
/// visibility and mutability so that the `state_implementation!` might dispatch these to the right
/// trait or implementation.
macro_rules! filter_methods {
// No more token.
($_:ident, ) => {};
// Match non mutable, public methods and output their signature.
(RoPublicMethodSignature, $(#[$meta:meta])* pub fn $fn:ident $(<$($template:tt),*>)? ( & $self_lifetime:lifetime $self_:tt $(, $name:ident : $type:ty)* $(,)? ) $(-> $ret:ty)* $body:block $($tail:tt)*) => {
$(#[$meta])* fn $fn $(<$($template),*>)?( & $self_lifetime $self_ $(, $name : $type)* ) $(-> $ret)*;
filter_methods!(RoPublicMethodSignature, $($tail)*);
};
(RoPublicMethodSignature, $(#[$meta:meta])* pub fn $fn:ident $(<$($template:tt),*>)? ( & $self_:tt $(, $name:ident : $type:ty)* $(,)? ) $(-> $ret:ty)* $body:block $($tail:tt)*) => {
$(#[$meta])* fn $fn $(<$($template),*>)?( & $self_ $(, $name : $type)* ) $(-> $ret)*;
filter_methods!(RoPublicMethodSignature, $($tail)*);
};
// Match non mutable, public methods and output it.
(RoPublicMethod, $(#[$meta:meta])* pub fn $fn:ident $(<$($template:tt),*>)? ( & $self_lifetime:lifetime $self_:tt $(, $name:ident : $type:ty)* $(,)? ) $(-> $ret:ty)* $body:block $($tail:tt)*) => {
$(#[$meta])* fn $fn $(<$($template),*>)?( & $self_lifetime $self_ $(, $name : $type)* ) $(-> $ret)* $body
filter_methods!(RoPublicMethod, $($tail)*);
};
(RoPublicMethod, $(#[$meta:meta])* pub fn $fn:ident $(<$($template:tt),*>)? ( & $self_:tt $(, $name:ident : $type:ty)* $(,)? ) $(-> $ret:ty)* $body:block $($tail:tt)*) => {
$(#[$meta])* fn $fn $(<$($template),*>)?( & $self_ $(, $name : $type)* ) $(-> $ret)* $body
filter_methods!(RoPublicMethod, $($tail)*);
};
// Match non mutable, private methods and output it.
(RoPrivateMethod, $(#[$meta:meta])* fn $fn:ident $(<$($template:tt),*>)? ( & $self_lifetime:lifetime $self_:tt $(, $name:ident : $type:ty)* $(,)? ) $(-> $ret:ty)* $body:block $($tail:tt)*) => {
$(#[$meta])* fn $fn $(<$($template),*>)?( & $self_lifetime $self_ $(, $name : $type)* ) $(-> $ret)* $body
filter_methods!(RoPrivateMethod, $($tail)*);
};
(RoPrivateMethod, $(#[$meta:meta])* fn $fn:ident $(<$($template:tt),*>)? ( & $self_:tt $(, $name:ident : $type:ty)* $(,)? ) $(-> $ret:ty)* $body:block $($tail:tt)*) => {
$(#[$meta])* fn $fn $(<$($template),*>)?( & $self_ $(, $name : $type)* ) $(-> $ret)* $body
filter_methods!(RoPrivateMethod, $($tail)*);
};
// Match mutable methods and output it.
(RwMethod, $(#[$meta:meta])* $vis:vis fn $fn:ident $(<$($template:tt),*>)? ( & $self_lifetime:lifetime mut $self_:tt $(, $name:ident : $type:ty)* $(,)? ) $(-> $ret:ty)* $body:block $($tail:tt)*) => {
$(#[$meta])* $vis fn $fn $(<$($template),*>)?( & $self_lifetime mut $self_ $(, $name : $type)* ) $(-> $ret)* $body
filter_methods!(RwMethod, $($tail)*);
};
(RwMethod, $(#[$meta:meta])* $vis:vis fn $fn:ident $(<$($template:tt),*>)? ( & mut $self_:tt $(, $name:ident : $type:ty)* $(,)? ) $(-> $ret:ty)* $body:block $($tail:tt)*) => {
$(#[$meta])* $vis fn $fn $(<$($template),*>)?( & mut $self_ $(, $name : $type)* ) $(-> $ret)* $body
filter_methods!(RwMethod, $($tail)*);
};
// Next 4 patterns match every type of method. They are used to remove the tokens associated
// with a method that has not been match by the previous patterns.
($qualifier:ident, $(#[$meta:meta])* $(pub)? fn $fn:ident $(<$($template:tt),*>)? ( & $self_lifetime:lifetime $self_:tt $(, $name:ident : $type:ty)* $(,)? ) $(-> $ret:ty)* $body:block $($tail:tt)*) => {
filter_methods!($qualifier, $($tail)*);
};
($qualifier:ident, $(#[$meta:meta])* $(pub)? fn $fn:ident $(<$($template:tt),*>)? ( & $self_lifetime:lifetime mut $self_:tt $(, $name:ident : $type:ty)* $(,)? ) $(-> $ret:ty)* $body:block $($tail:tt)*) => {
filter_methods!($qualifier, $($tail)*);
};
($qualifier:ident, $(#[$meta:meta])* $(pub)? fn $fn:ident $(<$($template:tt),*>)? ( & $self_:tt $(, $name:ident : $type:ty)* $(,)? ) $(-> $ret:ty)* $body:block $($tail:tt)*) => {
filter_methods!($qualifier, $($tail)*);
};
($qualifier:ident, $(#[$meta:meta])* $(pub)? fn $fn:ident $(<$($template:tt),*>)? ( &mut $self_:tt $(, $name:ident : $type:ty)* $(,)? ) $(-> $ret:ty)* $body:block $($tail:tt)*) => {
filter_methods!($qualifier, $($tail)*);
};
}
// Public re-export of macros allows them to be used like regular rust items.
pub(crate) use filter_methods;
pub(crate) use state_accessor;
pub(crate) use state_implementation;
#[cfg(test)]
mod test {
use super::*;
pub struct FooMutableState {
y: i32,
}
pub struct Foo {
x: i32,
mutable_state: RwLock<FooMutableState>,
}
impl Foo {
fn new() -> Self {
Self { x: 2, mutable_state: RwLock::new(FooMutableState { y: 3 }) }
}
state_accessor!(Foo, mutable_state);
}
state_implementation!(Foo, FooMutableState, {
// Some comment
fn x_and_y(&self) -> i32 {
self.base().x + self.y
}
/// Some rustdoc.
pub fn pub_x_and_y(&self) -> i32 {
self.x_and_y()
}
fn do_something(&self) {}
fn set_y(&mut self, other_y: i32) {
self.y = other_y;
}
pub fn pub_set_y(&mut self, other_y: i32) {
self.set_y(other_y)
}
fn do_something_mutable(&mut self) {
self.do_something();
}
pub fn with_lifecycle<'a>(&self, _n: &'a u32) {}
pub fn with_type<T>(&self, _n: &T) {}
pub fn with_lifecycle_and_type<'a, T>(&self, _n: &'a T) {}
pub fn with_lifecycle_on_self<'a, T>(&'a self, _n: &'a T) {}
});
fn take_foo_state<'a>(foo_state: &impl FooReadGuard<'a>) -> i32 {
foo_state.pub_x_and_y()
}
#[::fuchsia::test]
fn test_generation() {
let foo = Arc::new(Foo::new());
assert_eq!(foo.read().x_and_y(), 5);
assert_eq!(foo.read().pub_x_and_y(), 5);
assert_eq!(foo.write().pub_x_and_y(), 5);
foo.write().set_y(22);
assert_eq!(foo.read().pub_x_and_y(), 24);
assert_eq!(foo.write().pub_x_and_y(), 24);
foo.write().pub_set_y(20);
assert_eq!(take_foo_state(&foo.read()), 22);
assert_eq!(take_foo_state(&foo.write()), 22);
foo.read().do_something();
foo.write().do_something();
foo.write().do_something_mutable();
}
}