ayun-schedule 0.21.0

The RUST Framework for Web Rustceans.
use crate::{
    task::{Handler, Task},
    Schedule,
};
use ayun_core::{
    app,
    traits::{ApplicationTrait, ServiceTrait},
};
use ayun_runtime::Runtime;

impl ServiceTrait for Schedule {
    type Item = Task;

    fn new() -> Self {
        Self::default()
    }

    fn register(mut self, item: Self::Item) -> Self {
        self.tasks.push(item);

        self
    }

    fn init<A: ApplicationTrait>() -> Self {
        (&A::with_schedule() as &dyn std::any::Any)
            .downcast_ref::<Self>()
            .cloned()
            .unwrap_or(Self::default())
    }

    fn run(self) -> Result<(), crate::Error> {
        let sync_tasks = self
            .tasks()
            .iter()
            .filter(|task| !task.handler().is_async())
            .cloned()
            .collect::<Vec<_>>();

        if !sync_tasks.is_empty() {
            std::thread::spawn(|| foreground(sync_tasks));
        }

        let async_tasks = self
            .tasks()
            .iter()
            .filter(|task| task.handler().is_async())
            .cloned()
            .collect::<Vec<_>>();

        if !async_tasks.is_empty() {
            let runtime = app().resolve::<Runtime>()?;

            runtime.spawn(background(async_tasks));
        }

        Ok(())
    }
}

fn foreground(mut tasks: Vec<Task>) {
    loop {
        for task in &mut tasks {
            if !task.available() {
                continue;
            }

            if let Handler::Sync(handler) = task.handler() {
                handler();
                task.set_last_tick(chrono::Local::now());
            }
        }

        std::thread::sleep(delay(&tasks));
    }
}

async fn background(mut tasks: Vec<Task>) {
    loop {
        for task in &mut tasks {
            if !task.available() {
                continue;
            }

            if let Handler::Async(handler) = task.handler() {
                handler().await;
                task.set_last_tick(chrono::Local::now());
            }
        }

        tokio::time::sleep(delay(&tasks)).await;
    }
}

fn delay(tasks: &[Task]) -> std::time::Duration {
    tasks
        .iter()
        .map(|task| task.delay())
        .min()
        .unwrap_or(std::time::Duration::from_millis(500))
}