shinyframework_application 0.1.2

Shiny Application
Documentation
use std::str::FromStr;
use std::sync::Arc;
use std::sync::atomic::{AtomicI64, Ordering};
use std::time::Duration;
use tokio::select;
use shiny_application::{Application, CancellationSignalFactory, CreateInfrastructureConfigurationError, ConfigurationFactory, Module};
use shiny_application::controllers::Controllers;
use shiny_common::pointer_utils::ToArc;
use shiny_common::context::Context;
use shiny_common::error::ServiceError;
use shiny_configuration::Configuration;
use shiny_configuration::configuration_provider::json5_provider::Json5Provider;
use shiny_jobs::Job;

#[tokio::test]
async fn test_application() {
    let counter = AtomicI64::new(0).arc();

    let mut application = Application::new(counter.clone(), TestModule);
    application.set_infrastructure_configuration_factory(TestableConfigurationFactory(r#"{
        jobs: {
            hello_world: {
                schedule: "PT1S",
            },
        }
    }"#.to_string()).arc());
    application.set_cancellation_signal_factory(TimeoutCancellationToken(Duration::from_secs(3)).arc());
    application.set_dynamic_configuration_factory(TestableConfigurationFactory(
        r#"{}"#.to_string()
    ).arc());

    select! {
        _ = application.run() => {
        }
        _ = tokio::time::sleep(Duration::from_secs(5)) => {
            panic!("application is not finished after 7 seconds")
        }
    }
    let counter = counter.load(Ordering::SeqCst);
    assert!(counter >= 2 && counter <= 3, "counter = {counter}")
}

struct TestModule;

impl Module for TestModule {
    type Clients = Arc<AtomicI64>;

    fn create(&self, client: Self::Clients) -> Controllers {
        let mut controllers = Controllers::new();

        controllers.jobs().add("hello_world", JobExample(client.clone()).arc());

        controllers
    }
}


struct JobExample(Arc<AtomicI64>);

#[async_trait::async_trait]
impl Job for JobExample {
    async fn execute(&self, _: &mut Context) -> Result<(), ServiceError> {
        self.0.fetch_add(1, Ordering::SeqCst);
        Ok(())
    }
}


struct TimeoutCancellationToken(Duration);

#[async_trait::async_trait]
impl CancellationSignalFactory for TimeoutCancellationToken {
    async fn cancellation_signal(&self) -> std::io::Result<()> {
        tokio::time::sleep(self.0).await;
        Ok(())
    }
}

struct TestableConfigurationFactory(String);

impl ConfigurationFactory for TestableConfigurationFactory {
    fn configuration(&self) -> Result<Configuration, CreateInfrastructureConfigurationError> {
        let root_provider = Json5Provider::from_str(&self.0).unwrap();

        Ok(
            Configuration::builder(root_provider)
                .build()?
        )
    }
}