1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
#![doc(test(attr(deny(warnings))))]
#![doc(test(attr(allow(dead_code))))]
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::ToTokens;
use syn::parse_macro_input;
macro_rules! bail {
($span:expr, $($tt:tt)*) => {
return Err(syn::Error::new_spanned($span, format!($($tt)*)))
};
}
mod volatile;
/// A derive macro for method-based accesses to volatile structures.
///
/// This macro allows you to access the fields of a volatile structure via methods that enforce access limitations.
/// It is also more easily chainable than `map_field`.
///
/// <div class="warning">
///
/// This macro generates and implements a new `{T}VolatileFieldAccess` trait, that you have to import if used from other modules.
/// Currently, the trait is only implemented for `VolatilePtr<'_, _, ReadWrite>`.
///
/// </div>
///
/// # Examples
///
/// ```
/// use volatile::access::ReadOnly;
/// use volatile::{VolatileFieldAccess, VolatileRef};
///
/// #[repr(C)]
/// #[derive(VolatileFieldAccess, Default)]
/// pub struct DeviceConfig {
/// feature_select: u32,
/// #[access(ReadOnly)]
/// feature: u32,
/// }
///
/// let mut device_config = DeviceConfig::default();
/// let mut volatile_ref = VolatileRef::from_mut_ref(&mut device_config);
/// let volatile_ptr = volatile_ref.as_mut_ptr();
///
/// volatile_ptr.feature_select().write(42);
/// assert_eq!(volatile_ptr.feature_select().read(), 42);
///
/// // This does not compile, because we specified `#[access(ReadOnly)]` for this field.
/// // volatile_ptr.feature().write(42);
///
/// // A real device might have changed the value, though.
/// assert_eq!(volatile_ptr.feature().read(), 0);
///
/// // You can also use shared references.
/// let volatile_ptr = volatile_ref.as_ptr();
/// assert_eq!(volatile_ptr.feature_select().read(), 42);
/// // This does not compile, because `volatile_ptr` is `ReadOnly`.
/// // volatile_ptr.feature_select().write(42);
/// ```
///
/// # Details
///
/// This macro generates a new trait (`{T}VolatileFieldAccess`) and implements it for `VolatilePtr<'a, T, ReadWrite>`.
/// The example above results in (roughly) the following code:
///
/// ```
/// # #[repr(C)]
/// # pub struct DeviceConfig {
/// # feature_select: u32,
/// # feature: u32,
/// # }
/// use volatile::access::{ReadOnly, ReadWrite, RestrictAccess};
/// use volatile::{map_field, VolatilePtr};
///
/// pub trait DeviceConfigVolatileFieldAccess<'a, A> {
/// fn feature_select(self) -> VolatilePtr<'a, u32, A::Restricted>
/// where
/// A: RestrictAccess<ReadWrite>;
///
/// fn feature(self) -> VolatilePtr<'a, u32, A::Restricted>
/// where
/// A: RestrictAccess<ReadOnly>;
/// }
///
/// impl<'a, A> DeviceConfigVolatileFieldAccess<'a, A> for VolatilePtr<'a, DeviceConfig, A> {
/// fn feature_select(self) -> VolatilePtr<'a, u32, A::Restricted>
/// where
/// A: RestrictAccess<ReadWrite>
/// {
/// map_field!(self.feature_select).restrict()
/// }
///
/// fn feature(self) -> VolatilePtr<'a, u32, A::Restricted>
/// where
/// A: RestrictAccess<ReadOnly>
/// {
/// map_field!(self.feature).restrict()
/// }
/// }
/// ```
#[proc_macro_derive(VolatileFieldAccess, attributes(access))]
pub fn derive_volatile(item: TokenStream) -> TokenStream {
match volatile::derive_volatile(parse_macro_input!(item)) {
Ok(items) => {
let mut tokens = TokenStream2::new();
for item in &items {
item.to_tokens(&mut tokens);
}
tokens.into()
}
Err(e) => e.to_compile_error().into(),
}
}