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
use anyhow::Result;
use esp_idf_hal::reset::restart;
use log::error;
use std::thread;
use crate::time::sleep;
/// Handles program failure by restarting the device.
///
/// This function waits for a second and then restarts the device if the program encounters an error.
pub fn failure() -> ! {
// This program should run forever, until the device is powered off.
// If something goes wrong and the program dies, we wait for a second and
// then restart the device.
sleep(1000);
restart();
}
/// Runs the main application logic with automatic error logging and device restart on exit.
///
/// This function wraps the provided closure to ensure the device restarts
/// if the program exits. Any errors are logged with their full chain
/// before the restart occurs.
///
/// # Arguments
/// * `f` - A closure that returns a `Result`.
///
/// # Type Parameters
/// * `F` - The type of the closure.
///
/// # Returns
/// Never returns normally - either runs forever or restarts the device.
pub fn main<F>(f: F) -> !
where
F: FnOnce() -> Result<()>,
{
if let Err(e) = f() {
error!("Fatal error: {:#}", e);
}
failure()
}
/// A guard that ensures the program restarts on thread exit.
struct ExitGuard;
impl Drop for ExitGuard {
/// Ensures the program restarts when the thread exits.
fn drop(&mut self) {
failure();
}
}
/// Spawns a new thread with a failure guard.
///
/// # Arguments
/// * `f` - A closure to execute in the new thread.
///
/// # Returns
/// A `JoinHandle` for the spawned thread.
///
/// # Type Parameters
/// * `F` - The type of the closure.
/// * `T` - The return type of the closure.
pub fn spawn<F, T>(f: F) -> thread::JoinHandle<T>
where
F: FnOnce() -> T + Send + 'static,
T: Send + 'static,
{
thread::spawn(move || {
let _guard = ExitGuard;
f()
})
}