veecle_os_runtime_macros/
lib.rs

1//! This crate provides runtime macros.
2
3#![forbid(unsafe_code)]
4
5mod actor;
6mod storable;
7
8#[allow(missing_docs)]
9// Documentation is defined on the reexport in `../../src/actor.rs` to allow using intra-doc-links.
10#[proc_macro_attribute]
11pub fn actor(
12    meta: proc_macro::TokenStream,
13    item: proc_macro::TokenStream,
14) -> proc_macro::TokenStream {
15    actor2(meta.into(), item.into()).into()
16}
17
18/// `proc_macro2` implementation of [`actor()`] to allow executing outside the compiler.
19///
20/// The actual implementation is in the module, this just maps any errors into `compile_error!`s to allow using `?` in
21/// the implementation while giving the expected infallible function signature.
22fn actor2(
23    meta: proc_macro2::TokenStream,
24    item: proc_macro2::TokenStream,
25) -> proc_macro2::TokenStream {
26    actor::impl_actor(meta, item).unwrap_or_else(|error| error.into_compile_error())
27}
28
29#[allow(missing_docs)]
30// Documentation is defined on the reexport in `../../src/datastore/slot/mod.rs` to allow using intra-doc-links.
31#[proc_macro_derive(Storable, attributes(storable))]
32pub fn derive_storable(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
33    derive_storable2(input.into()).into()
34}
35
36/// `proc_macro2` implementation of [`derive_storable`] to allow executing outside the compiler.
37///
38/// The actual implementation is in the module, this just maps any errors into `compile_error!`s to allow using `?` in
39/// the implementation while giving the expected infallible function signature.
40fn derive_storable2(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
41    storable::impl_derive_storable(input).unwrap_or_else(|error| error.write_errors())
42}
43
44/// Returns a path to the `veecle_os_runtime` crate for use when macro users don't set it explicitly.
45fn veecle_os_runtime_path() -> syn::Result<syn::Path> {
46    proc_macro_crate::crate_name("veecle-os-runtime")
47        .map(|found| match found {
48            proc_macro_crate::FoundCrate::Itself => {
49                // The only place we use `veecle-os-runtime` within "itself" is doc-tests, where it needs to be an external
50                // path anyway.
51                syn::parse_quote!(::veecle_os_runtime)
52            }
53            proc_macro_crate::FoundCrate::Name(name) => {
54                let ident = syn::Ident::new(&name, proc_macro2::Span::call_site());
55                syn::parse_quote!(::#ident)
56            }
57        })
58        .or_else(|_| {
59            proc_macro_crate::crate_name("veecle-os").map(|found| match found {
60                proc_macro_crate::FoundCrate::Itself => {
61                    todo!("unused currently, not sure what behavior will be wanted")
62                }
63                proc_macro_crate::FoundCrate::Name(name) => {
64                    let ident = syn::Ident::new(&name, proc_macro2::Span::call_site());
65                    syn::parse_quote!(::#ident::runtime)
66                }
67            })
68        })
69        .map_err(|_| {
70            syn::Error::new(
71                proc_macro2::Span::call_site(),
72                "could not find either veecle-os-runtime or veecle-os crates",
73            )
74        })
75}
76
77#[cfg(test)]
78mod tests {
79    use std::fs::File;
80
81    #[test]
82    fn test_for_code_coverage() -> Result<(), Box<dyn std::error::Error>> {
83        for entry in walkdir::WalkDir::new("tests/ui") {
84            let entry = entry?;
85            if entry.path().extension().unwrap_or_default() == "rs" {
86                runtime_macros::emulate_attributelike_macro_expansion(
87                    File::open(entry.path())?,
88                    &[
89                        ("actor", super::actor2),
90                        ("veecle_os_runtime::actor", super::actor2),
91                        ("veecle_os_runtime_macros::actor", super::actor2),
92                    ],
93                )?;
94                runtime_macros::emulate_derive_macro_expansion(
95                    File::open(entry.path())?,
96                    &[
97                        ("Storable", super::derive_storable2),
98                        ("veecle_os_runtime::Storable", super::derive_storable2),
99                        (
100                            "veecle_os_runtime_macros::Storable",
101                            super::derive_storable2,
102                        ),
103                    ],
104                )?;
105            }
106        }
107
108        Ok(())
109    }
110}