veecle_os_runtime_macros/lib.rs
1//! This crate provides runtime macros.
2
3#![forbid(unsafe_code)]
4#![cfg_attr(coverage_nightly, feature(coverage_attribute))]
5
6mod actor;
7mod storable;
8
9/// Generates an [`Actor`] from a function.
10///
11/// [`Actor`]: https://docs.rs/veecle-os/latest/veecle_os/runtime/trait.Actor.html
12///
13/// ```rust
14/// use veecle_os_runtime::{Reader, Writer};
15/// # use std::convert::Infallible;
16/// # use veecle_os_runtime::Storable;
17/// #
18/// # #[derive(Debug, PartialEq, Clone, Default, Storable)]
19/// # pub struct Sensor(pub u8);
20///
21/// #[veecle_os_runtime::actor]
22/// async fn macro_test_actor(
23/// _sensor_reader: Reader<'_, Sensor>,
24/// _sensor_writer: Writer<'_, Sensor>,
25/// #[init_context] _my_init_context: u32,
26/// ) -> Infallible {
27/// loop {
28/// // Do things.
29/// }
30/// }
31/// ```
32///
33/// # Attribute Arguments
34///
35/// ## `crate`
36///
37/// If necessary the path to [`veecle-os-runtime`] can be overridden by passing a `crate = ::some::path` argument.
38///
39/// [`veecle-os-runtime`]: https://docs.rs/veecle-os-runtime/latest/veecle_os_runtime/
40///
41/// ```rust
42/// extern crate veecle_os_runtime as my_veecle_os_runtime;
43///
44/// use my_veecle_os_runtime::{Reader, Writer};
45/// # use std::convert::Infallible;
46/// # use my_veecle_os_runtime::Storable;
47/// #
48/// # #[derive(Debug, PartialEq, Clone, Default, Storable)]
49/// # pub struct Sensor(pub u8);
50///
51/// #[my_veecle_os_runtime::actor(crate = my_veecle_os_runtime)]
52/// async fn macro_test_actor(
53/// _sensor_reader: Reader<'_, Sensor>,
54/// _sensor_writer: Writer<'_, Sensor>,
55/// #[init_context] _my_init_context: u32,
56/// ) -> Infallible {
57/// loop {
58/// // Do things.
59/// }
60/// }
61/// ```
62#[proc_macro_attribute]
63pub fn actor(
64 meta: proc_macro::TokenStream,
65 item: proc_macro::TokenStream,
66) -> proc_macro::TokenStream {
67 actor2(meta.into(), item.into()).into()
68}
69
70/// `proc_macro2` implementation of [`actor()`] to allow executing outside the compiler.
71///
72/// The actual implementation is in the module, this just maps any errors into `compile_error!`s to allow using `?` in
73/// the implementation while giving the expected infallible function signature.
74fn actor2(
75 meta: proc_macro2::TokenStream,
76 item: proc_macro2::TokenStream,
77) -> proc_macro2::TokenStream {
78 actor::impl_actor(meta, item).unwrap_or_else(|error| error.into_compile_error())
79}
80
81/// Implements [`Storable`] for a struct or enum.
82///
83/// # Attributes
84///
85/// * `data_type = "Type"`: Sets the [`Storable::DataType`]. Defaults to `Self`.
86/// * `crate = ::veecle_os_runtime`: Overrides the path to the `veecle-os-runtime` crate in case the import was renamed.
87///
88/// [`Storable`]: https://docs.rs/veecle-os/latest/veecle_os/runtime/trait.Storable.html
89/// [`Storable::DataType`]: https://docs.rs/veecle-os/latest/veecle_os/runtime/trait.Storable.html#associatedtype.DataType
90///
91/// ```
92/// use core::fmt::Debug;
93/// use veecle_os_runtime::Storable;
94///
95/// // `DataType = Self`
96/// #[derive(Debug, Storable)]
97/// pub struct Sensor<T>
98/// where
99/// T: Debug,
100/// {
101/// test: u8,
102/// test0: u8,
103/// test1: T,
104/// }
105///
106/// // `DataType = Self`
107/// #[derive(Debug, Storable)]
108/// pub struct Motor {
109/// test: u8,
110/// }
111///
112/// // `DataType = Self`
113/// #[derive(Debug, Storable)]
114/// pub enum Actuator {
115/// Variant1,
116/// Variant2(u8),
117/// Variant3 { test: u8 },
118/// }
119///
120/// // `DataType = u8`
121/// #[derive(Storable)]
122/// #[storable(data_type = "u8")]
123/// pub struct EventId;
124/// ```
125#[proc_macro_derive(Storable, attributes(storable))]
126pub fn derive_storable(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
127 derive_storable2(input.into()).into()
128}
129
130/// `proc_macro2` implementation of [`derive_storable`] to allow executing outside the compiler.
131///
132/// The actual implementation is in the module, this just maps any errors into `compile_error!`s to allow using `?` in
133/// the implementation while giving the expected infallible function signature.
134fn derive_storable2(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
135 storable::impl_derive_storable(input).unwrap_or_else(|error| error.write_errors())
136}
137
138/// Returns a path to the `veecle_os_runtime` crate for use when macro users don't set it explicitly.
139fn veecle_os_runtime_path() -> syn::Result<syn::Path> {
140 proc_macro_crate::crate_name("veecle-os-runtime")
141 .map(|found| match found {
142 proc_macro_crate::FoundCrate::Itself => {
143 // The only place we use `veecle-os-runtime` within "itself" is doc-tests, where it needs to be an external
144 // path anyway.
145 syn::parse_quote!(::veecle_os_runtime)
146 }
147 proc_macro_crate::FoundCrate::Name(name) => {
148 let ident = syn::Ident::new(&name, proc_macro2::Span::call_site());
149 syn::parse_quote!(::#ident)
150 }
151 })
152 .or_else(|_| {
153 proc_macro_crate::crate_name("veecle-os").map(|found| match found {
154 proc_macro_crate::FoundCrate::Itself => {
155 todo!("unused currently, not sure what behavior will be wanted")
156 }
157 proc_macro_crate::FoundCrate::Name(name) => {
158 let ident = syn::Ident::new(&name, proc_macro2::Span::call_site());
159 syn::parse_quote!(::#ident::runtime)
160 }
161 })
162 })
163 .map_err(|_| {
164 syn::Error::new(
165 proc_macro2::Span::call_site(),
166 "could not find either veecle-os-runtime or veecle-os crates",
167 )
168 })
169}
170
171#[cfg(test)]
172#[cfg_attr(coverage_nightly, coverage(off))]
173mod tests {
174 use std::fs::File;
175
176 #[test]
177 fn test_for_code_coverage() -> Result<(), Box<dyn std::error::Error>> {
178 for entry in walkdir::WalkDir::new("tests/ui") {
179 let entry = entry?;
180 if entry.path().extension().unwrap_or_default() == "rs" {
181 runtime_macros::emulate_attributelike_macro_expansion(
182 File::open(entry.path())?,
183 &[
184 ("actor", super::actor2),
185 ("veecle_os_runtime::actor", super::actor2),
186 ("veecle_os_runtime_macros::actor", super::actor2),
187 ],
188 )?;
189 runtime_macros::emulate_derive_macro_expansion(
190 File::open(entry.path())?,
191 &[
192 ("Storable", super::derive_storable2),
193 ("veecle_os_runtime::Storable", super::derive_storable2),
194 (
195 "veecle_os_runtime_macros::Storable",
196 super::derive_storable2,
197 ),
198 ],
199 )?;
200 }
201 }
202
203 Ok(())
204 }
205}