radrootsd

JSON-RPC bridge for Radroots event publishing
git clone https://radroots.dev/git/radrootsd.git
Log | Files | Refs | README | LICENSE

main.rs (2883B)


      1 #![forbid(unsafe_code)]
      2 #![cfg_attr(coverage_nightly, feature(coverage_attribute))]
      3 
      4 use std::process::ExitCode;
      5 
      6 use anyhow::Result;
      7 
      8 #[cfg(not(test))]
      9 #[cfg_attr(coverage_nightly, coverage(off))]
     10 #[tokio::main]
     11 async fn main() -> ExitCode {
     12     exit_code_from_run(run().await)
     13 }
     14 
     15 #[cfg(test)]
     16 fn main() -> ExitCode {
     17     exit_code_from_run(Ok(()))
     18 }
     19 
     20 fn exit_code_from_run(result: Result<()>) -> ExitCode {
     21     match result {
     22         Ok(()) => ExitCode::SUCCESS,
     23         Err(err) => {
     24             tracing::error!(error = ?err, "Fatal error");
     25             eprintln!("Fatal error: {err:#}");
     26             ExitCode::FAILURE
     27         }
     28     }
     29 }
     30 
     31 #[cfg(test)]
     32 static RUN_HOOK: std::sync::OnceLock<std::sync::Mutex<Option<Result<(), String>>>> =
     33     std::sync::OnceLock::new();
     34 
     35 #[cfg(test)]
     36 fn run_hook() -> &'static std::sync::Mutex<Option<Result<(), String>>> {
     37     RUN_HOOK.get_or_init(|| std::sync::Mutex::new(None))
     38 }
     39 
     40 #[cfg(test)]
     41 fn take_run_hook_result() -> Option<Result<(), String>> {
     42     run_hook()
     43         .lock()
     44         .unwrap_or_else(std::sync::PoisonError::into_inner)
     45         .take()
     46 }
     47 
     48 #[cfg(test)]
     49 async fn run() -> Result<()> {
     50     if let Some(result) = take_run_hook_result() {
     51         return result.map_err(anyhow::Error::msg);
     52     }
     53     Err(anyhow::anyhow!("run hook not set"))
     54 }
     55 
     56 #[cfg(not(test))]
     57 #[cfg_attr(coverage_nightly, coverage(off))]
     58 async fn run() -> Result<()> {
     59     radrootsd::app::run().await
     60 }
     61 
     62 #[cfg(test)]
     63 #[cfg_attr(coverage_nightly, coverage(off))]
     64 mod tests {
     65     use super::{exit_code_from_run, main, run, run_hook};
     66     use std::process::ExitCode;
     67     use std::sync::Mutex;
     68 
     69     static TEST_LOCK: Mutex<()> = Mutex::new(());
     70 
     71     fn test_guard() -> std::sync::MutexGuard<'static, ()> {
     72         let guard = TEST_LOCK
     73             .lock()
     74             .unwrap_or_else(std::sync::PoisonError::into_inner);
     75         *run_hook()
     76             .lock()
     77             .unwrap_or_else(std::sync::PoisonError::into_inner) = None;
     78         guard
     79     }
     80 
     81     #[test]
     82     fn exit_code_from_run_maps_success_and_error() {
     83         assert_eq!(exit_code_from_run(Ok(())), ExitCode::SUCCESS);
     84         assert_eq!(
     85             exit_code_from_run(Err(anyhow::anyhow!("boom"))),
     86             ExitCode::FAILURE
     87         );
     88     }
     89 
     90     #[test]
     91     fn main_returns_success_in_test_build() {
     92         assert_eq!(main(), ExitCode::SUCCESS);
     93     }
     94 
     95     #[tokio::test]
     96     async fn run_returns_error_when_hook_is_missing() {
     97         let _guard = test_guard();
     98         let err = run().await.expect_err("hook missing should error");
     99         let msg = format!("{err:#}");
    100         assert!(msg.contains("run hook not set"));
    101     }
    102 
    103     #[tokio::test]
    104     async fn run_uses_hook_result() {
    105         let _guard = test_guard();
    106         *run_hook()
    107             .lock()
    108             .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(Ok(()));
    109         assert!(run().await.is_ok());
    110     }
    111 }