Skip to main content

anodized_core/instrument/
mod.rs

1use proc_macro2::TokenStream;
2use quote::{ToTokens, quote};
3use syn::{Attribute, Meta};
4
5pub mod fns;
6pub mod traits;
7
8pub struct Backend {
9    pub build_check: fn(Option<&Meta>, &TokenStream, &str, &TokenStream) -> TokenStream,
10}
11
12impl Backend {
13    pub const CHECK_AND_PANIC: Backend = Backend {
14        build_check: build_assert,
15    };
16
17    pub const CHECK_AND_PRINT: Backend = Backend {
18        build_check: build_eprint,
19    };
20
21    pub const NO_CHECK: Backend = Backend {
22        build_check: build_inert,
23    };
24}
25
26/// Make an error message to say that some item is unsupported.
27pub fn make_item_error<T: ToTokens>(tokens: &T, item_descr: &str) -> syn::Error {
28    let msg = format!(
29        r#"The #[spec] attribute doesn't yet support this item: {}.
30If this is a problem for your use case, please open a feature
31request at https://github.com/mkovaxx/anodized/issues/new"#,
32        item_descr
33    );
34    syn::Error::new_spanned(tokens, msg)
35}
36
37/// Finds the `[spec]` attrib in an attribute list.
38///
39/// Returns the spec [Attribute] and the remaining attributes.
40fn find_spec_attr(attrs: Vec<Attribute>) -> syn::Result<(Option<Attribute>, Vec<Attribute>)> {
41    let mut spec_attr = None;
42    let mut other_attrs = Vec::new();
43
44    for attr in attrs {
45        if attr.path().is_ident("spec") {
46            if spec_attr.is_some() {
47                return Err(syn::Error::new_spanned(
48                    attr,
49                    "multiple `#[spec]` attributes on a single item are not supported",
50                ));
51            }
52            spec_attr = Some(attr);
53        } else {
54            other_attrs.push(attr);
55        }
56    }
57
58    Ok((spec_attr, other_attrs))
59}
60
61fn build_assert(
62    cfg: Option<&Meta>,
63    expr: &TokenStream,
64    message: &str,
65    repr: &TokenStream,
66) -> TokenStream {
67    let repr_str = repr.to_string();
68    let check = quote! { assert!(#expr, #message, #repr_str); };
69    guard_check(cfg, check)
70}
71
72fn build_eprint(
73    cfg: Option<&Meta>,
74    expr: &TokenStream,
75    message: &str,
76    repr: &TokenStream,
77) -> TokenStream {
78    let repr_str = repr.to_string();
79    let check = quote! {
80        if !(#expr) {
81            eprintln!(#message, #repr_str);
82        }
83    };
84    guard_check(cfg, check)
85}
86
87fn build_inert(
88    // The check will not be present at runtime regardless of the `#[cfg]` setting.
89    _cfg: Option<&Meta>,
90    expr: &TokenStream,
91    message: &str,
92    repr: &TokenStream,
93) -> TokenStream {
94    let repr_str = repr.to_string();
95    quote! {
96        if false {
97            assert!(#expr, #message, #repr_str);
98        }
99    }
100}
101
102fn guard_check(cfg: Option<&Meta>, check: TokenStream) -> TokenStream {
103    if let Some(cfg) = cfg {
104        quote! { if cfg!(#cfg) { #check } }
105    } else {
106        check
107    }
108}