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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
use crate::runtime::action::builtin::remote::RemoteHttpAction;
use crate::runtime::action::{Impl, ImplRemote, Tick};
use crate::runtime::args::RtArgs;
use crate::runtime::context::{TreeContextRef, TreeRemoteContextRef};
use crate::runtime::{RtResult, RuntimeError, TickResult};
use std::collections::HashMap;
use std::time::{Duration, SystemTime};

/// The action that represent the stubs fro the real actions.
pub enum SimAction {
    /// usize here is a millisecond for delay
    Success(usize),
    /// usize here is a millisecond for delay
    Random(usize),
    /// usize here is a millisecond for delay
    Failure(usize),
    /// The remote action wrapper
    Remote(RemoteHttpAction),
}

impl SimAction {
    pub fn is_remote(&self) -> bool {
        match self {
            SimAction::Remote(_) => true,
            _ => false,
        }
    }

    pub fn create(key: &str, params: HashMap<String, String>) -> RtResult<SimAction> {
        let delay = params
            .get("delay")
            .map(|s| s.parse::<usize>().unwrap_or_default())
            .unwrap_or_default();

        match key {
            "success" => Ok(SimAction::Success(delay)),
            "random" => Ok(SimAction::Random(delay)),
            "failure" => Ok(SimAction::Failure(delay)),
            "remote" => {
                let url = params.get("url").cloned().ok_or_else(|| {
                    RuntimeError::WrongArgument("the url is not specified".to_string())
                })?;

                let action = if let Some(serv) = params.get("server").cloned() {
                    RemoteHttpAction::new_with(url, serv)
                } else {
                    RemoteHttpAction::new(url)
                };

                Ok(SimAction::Remote(action))
            }
            e => Err(RuntimeError::WrongArgument(format!(
                "the {e} is not recognized as a simulation stub."
            ))),
        }
    }
}

impl ImplRemote for SimAction {
    fn tick(&self, args: RtArgs, ctx: TreeRemoteContextRef) -> Tick {
        match self {
            SimAction::Remote(delegate) => delegate.tick(args, ctx),
            _ => Err(RuntimeError::uex(
                "the remote action is expected here".to_string(),
            )),
        }
    }
}

impl Impl for SimAction {
    fn tick(&self, _args: RtArgs, _ctx: TreeContextRef) -> Tick {
        match self {
            SimAction::Success(d) => {
                std::thread::sleep(Duration::from_millis(*d as u64));
                Ok(TickResult::success())
            }
            SimAction::Failure(d) => {
                std::thread::sleep(Duration::from_millis(*d as u64));
                Ok(TickResult::failure_empty())
            }
            SimAction::Random(d) => {
                std::thread::sleep(Duration::from_millis(*d as u64));
                let num = SystemTime::now()
                    .duration_since(SystemTime::UNIX_EPOCH)
                    .unwrap()
                    .as_millis();

                if num % 2 == 0 {
                    Ok(TickResult::success())
                } else {
                    Ok(TickResult::failure_empty())
                }
            }
            SimAction::Remote(_) => Ok(TickResult::Failure(
                "The remote action should execute another contract namely ImplRemote".to_string(),
            )),
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::runtime::action::Impl;
    use crate::runtime::args::RtArgs;
    use crate::runtime::blackboard::BlackBoard;
    use crate::runtime::context::TreeContextRef;
    use crate::runtime::trimmer::TrimmingQueue;
    use crate::runtime::TickResult;
    use crate::simulator::actions::SimAction;
    use crate::tracer::Tracer;
    use std::sync::{Arc, Mutex};
    use crate::runtime::env::RtEnv;

    #[test]
    fn smoke() {
        let action = SimAction::Success(0);
        let result = action.tick(
            RtArgs(vec![]),
            TreeContextRef::new(
                Arc::new(Mutex::new(BlackBoard::default())),
                Arc::new(Mutex::new(Tracer::default())),
                0,
                Arc::new(Mutex::new(TrimmingQueue::default())),
                Arc::new(Mutex::new(RtEnv::try_new().unwrap()))
            ),
        );

        assert!(result.is_ok());
        assert_eq!(result.unwrap(), TickResult::Success);
    }
}