radrootsd

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

main.rs (2871B)


      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::run_daemon().await.map_err(anyhow::Error::new)
     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 tokio::sync::{Mutex, MutexGuard};
     68 
     69     static TEST_LOCK: Mutex<()> = Mutex::const_new(());
     70 
     71     async fn test_guard() -> MutexGuard<'static, ()> {
     72         let guard = TEST_LOCK.lock().await;
     73         *run_hook()
     74             .lock()
     75             .unwrap_or_else(std::sync::PoisonError::into_inner) = None;
     76         guard
     77     }
     78 
     79     #[test]
     80     fn exit_code_from_run_maps_success_and_error() {
     81         assert_eq!(exit_code_from_run(Ok(())), ExitCode::SUCCESS);
     82         assert_eq!(
     83             exit_code_from_run(Err(anyhow::anyhow!("boom"))),
     84             ExitCode::FAILURE
     85         );
     86     }
     87 
     88     #[test]
     89     fn main_returns_success_in_test_build() {
     90         assert_eq!(main(), ExitCode::SUCCESS);
     91     }
     92 
     93     #[tokio::test]
     94     async fn run_returns_error_when_hook_is_missing() {
     95         let _guard = test_guard().await;
     96         let err = run().await.expect_err("hook missing should error");
     97         let msg = format!("{err:#}");
     98         assert!(msg.contains("run hook not set"));
     99     }
    100 
    101     #[tokio::test]
    102     async fn run_uses_hook_result() {
    103         let _guard = test_guard().await;
    104         *run_hook()
    105             .lock()
    106             .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(Ok(()));
    107         assert!(run().await.is_ok());
    108     }
    109 }