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
116
117
118
119
120
use darling::FromMeta;
use proc_macro2::TokenStream;
use quote::{quote, quote_spanned};
use syn::{spanned::Spanned, Error, ItemImpl, Result};

#[derive(Debug, FromMeta)]
pub struct AvionicsParameters {
    default: String,
    #[darling(default)]
    no_panic: bool,
}

pub fn harness(params: AvionicsParameters, input: &ItemImpl) -> Result<TokenStream> {
    let st = {
        let ItemImpl {
            self_ty, trait_, ..
        } = &input;

        let (invert, trait_, _) = &trait_
            .as_ref()
            .ok_or_else(|| Error::new(input.span(), "no trait was found to implement"))?;

        if !trait_.is_ident("Avionics") {
            return Err(Error::new(
                trait_.span(),
                "expected a trait implementation of `Avionics`",
            ));
        }

        if let Some(invert) = invert {
            return Err(Error::new(
                invert.span(),
                "cannot negate the `Avionics` implementation",
            ));
        }

        self_ty
    };

    let default = {
        let default: TokenStream = params.default.parse()?;

        quote_spanned! {params.default.span()=>
            #default
        }
    };

    let (avionics_impl, panic_impl) = if std::env::var_os("__PREFLIGHT").is_some() {
        (
            quote! {
                use preflight::abi::*;

                #[no_mangle]
                pub static avionics_guide: AvionicsGuide = |sensors: &Sensors| unsafe {
                    AVIONICS.guide(sensors)
                };

                static mut __PANIC_CALLBACK: Option<PanicCallback> = None;

                #[no_mangle]
                pub static set_panic_callback: SetPanicCallback = |callback: PanicCallback| unsafe {
                    __PANIC_CALLBACK.replace(callback);
                };
            },
            quote! {
                if let Some(callback) = unsafe { __PANIC_CALLBACK } {
                    callback(_panic_info, unsafe { &AVIONICS })
                }
            },
        )
    } else {
        (
            quote! {
                unsafe extern "C" fn avionics_guide(sensors: &Sensors) -> Control {
                    AVIONICS.guide(sensors)
                }
            },
            quote! {
                extern "C" {
                    fn panic_abort();
                }

                unsafe { panic_abort() };
            },
        )
    };

    let panic_handler = if params.no_panic {
        quote! {}
    } else {
        quote! {
            #[cfg(not(any(test, trybuild)))]
            #[panic_handler]
            fn handle_panic(_panic_info: &core::panic::PanicInfo) -> ! {
                #panic_impl

                loop {
                    core::sync::atomic::spin_loop_hint()
                }
            }
        }
    };

    Ok(quote! {
        #[doc(hidden)]
        mod __PREFLIGHT {
            use super::*;

            #[allow(unused)]
            static mut AVIONICS: #st = #default;

            #[no_mangle]
            static __PREFLIGHT: bool = cfg!(preflight);

            #avionics_impl

            #panic_handler
        }
    })
}