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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
//! This module contains the definition of a Plugin, and how to wrap it inside the Orchestrator
//!
use std::{error::Error, future::Future, marker::PhantomData, mem, sync::Arc};

use async_trait::async_trait;

use crate::prelude::*;

/// Plugin interface:
///
/// contains name and description methods, and they are needed for visualizzation purpose.
///
/// The fondamental method is run, that takes an OrchestratorReference.
/// It can gracefully shutdown all program by notifing "should_stop"
///
/// Then there is on_add, and is executed while the plugin is getting add.
pub trait Plugin<S: ExecutorGlobalState>: Sized + Send + Sync + 'static {
    /// Return the name of the Plugin, used for Error creation and Debug
    fn name(&self) -> &str;

    /// Return a descriptionn of the Plugin. It should contain a list of the registered and activated Executors
    fn desctiption(&self) -> &str;

    /// Function called when the Plugin is runned (at the end of the init phase).
    /// it takes an OrchestratorReference, which permit almost complete control over the orchestrator, and a shared Notify.
    /// This notify should get called when a Plugin request an orchestrator shutdown.
    fn run(
        self,
        o: OrchestratorReference<S>,
        should_stop: Arc<Notify>,
    ) -> impl Future<Output = ()> + Send + 'static {
        async {
            let _o = o;
            let _s = should_stop;
        }
    }
    /// When the plugin gets added to the Orchestrator, this function gets called.
    /// It gives complete control on the unrunned executor.
    /// It should be used to register/activate executor, add other Plugins, add Exercise (discouraged)...
    fn on_add<'a>(
        &'a mut self,
        o: &'a mut Orchestrator<S>,
    ) -> impl Future<Output = Result<(), Box<dyn Error + Send + Sync + 'static>>> + Send + 'a {
        async {
            let _o = o;
            Ok(())
        }
    }
}

/// inner plugin storage, is not exposed outside this crate
///
/// it's needed because it simplify some extension for the user
#[allow(dead_code)] // TODO REMOVE DEADCODE check, needed because some fields are never accessed
pub(crate) struct PluginStorage<T: Plugin<S>, S: ExecutorGlobalState> {
    pub name: String,
    pub description: String,
    pub inner: Option<T>,
    pub has_run: bool,
    ph: PhantomData<S>,
}

/// Wraps the future In a Pinned Box. necessary to render it Typesafe
#[async_trait]
pub(crate) trait InnerPlugin<S: ExecutorGlobalState>: Send + Sync {
    async fn run(
        &mut self,
        o: OrchestratorReference<S>,
        should_stop: Arc<Notify>,
    ) -> Result<(), Box<dyn Error>>;
}

/// how should run be wrapped
#[async_trait]
impl<T: Plugin<S> + 'static, S: ExecutorGlobalState> InnerPlugin<S> for PluginStorage<T, S> {
    async fn run(
        &mut self,
        o: OrchestratorReference<S>,
        should_stop: Arc<Notify>,
    ) -> Result<(), Box<dyn Error>> {
        let mut data = None;
        mem::swap(&mut self.inner, &mut data);
        let data = data.ok_or("cannot find function")?;
        data.run(o, should_stop).await;
        Ok(())
    }
}

impl<T: Plugin<S>, S: ExecutorGlobalState> PluginStorage<T, S> {
    /// create a new plugin storage
    pub fn new(inner: T) -> Self {
        let name = inner.name().to_string();
        let description = inner.desctiption().to_string();
        Self {
            name,
            description,
            inner: Some(inner),
            has_run: false,
            ph: PhantomData,
        }
    }
}