userd 0.2.0

A user daemon, managing services and regular running of jobs, in user space.
use std::{fs::File, path::Path};

mod activity;
mod conditions;
mod config;
mod prelude;
mod service;
mod task;
mod triggers;
mod utils;

use prelude::*;

struct Daemon {
    tasks: Vec<Task>,
    services: Vec<Service>,
}

impl Daemon {
    fn parse(path: impl AsRef<Path>) -> Result<Daemon> {
        let path = path.as_ref();
        if path.exists() {
            let config: config::Config = serde_yaml_ng::from_reader(File::open(path)?)?;
            let tasks = config
                .tasks
                .into_iter()
                .map(Task::new)
                .collect::<Result<_>>()?;
            let services = config
                .services
                .into_iter()
                .map(Service::new)
                .collect::<Result<_>>()?;
            Ok(Daemon { tasks, services })
        } else {
            Err(anyhow::anyhow!(
                "Missing configuration file at '{:?}'.",
                path
            ))
        }
    }
}

#[tokio::main]
async fn main() {
    env_logger::builder()
        .filter_level(log::LevelFilter::Info)
        .parse_default_env()
        .init();
    let dirs = directories::ProjectDirs::from("com", "cyloncore", "userd").unwrap();
    let config_path = dirs.config_dir().join("config");

    loop {
        let daemon = Daemon::parse(&config_path);
        match daemon {
            Ok(daemon) => {
                log::info!("Success!");
                let tasks: Vec<_> = daemon
                    .tasks
                    .into_iter()
                    .map(|task| tokio::spawn(task.start()))
                    .collect();
                let services: Vec<_> = daemon
                    .services
                    .into_iter()
                    .map(|service| tokio::spawn(service.start()))
                    .collect();
                utils::async_watch(config_path.clone()).await.unwrap();
                for t in tasks {
                    t.abort();
                }
                for s in services {
                    s.abort();
                }
            }
            Err(e) => {
                log::error!("Failure to start daemon: {:?}", e);
                utils::async_watch(config_path.clone()).await.unwrap();
            }
        }
    }
}