1use crate::{Result, UserActionStep, UserStep};
2use serde::{Deserialize, Serialize};
3use std::{collections::HashMap, fmt::Debug, sync::Arc};
4
5#[derive(Deserialize, Serialize, Debug, Clone)]
6pub struct ActionSteps {
7 pub pre: Option<UserStep>,
8 pub run: UserStep,
9 pub post: Option<UserStep>,
10}
11
12pub trait Action: Send + Sync {
13 fn normalize(&self, step: UserActionStep) -> Result<ActionSteps>;
14}
15
16pub type SharedActionDriver = Arc<ActionDriver>;
17
18#[derive(Clone)]
19pub struct ActionDriver {
20 actions: Arc<HashMap<String, Box<dyn Action>>>,
21}
22
23impl ActionDriver {
24 pub fn new(actions: HashMap<String, Box<dyn Action>>) -> Self {
25 Self {
26 actions: Arc::new(actions),
27 }
28 }
29
30 pub fn try_normalize(&self, step: UserActionStep) -> Result<Option<ActionSteps>> {
31 if let Some(action) = &self.actions.get(&step.uses) {
32 let normalized = action.normalize(step)?;
33
34 Ok(Some(normalized))
35 } else {
36 Ok(None)
37 }
38 }
39}
40
41#[cfg(test)]
42mod tests {
43 use super::*;
44 use crate::UserCommandStep;
45
46 #[test]
47 fn test_normalize_step_actions() -> Result<()> {
48 struct CacheAction {}
49
50 impl Action for CacheAction {
51 fn normalize(&self, _step: UserActionStep) -> Result<ActionSteps> {
52 Ok(ActionSteps {
53 pre: None,
54 run: UserStep::Command(UserCommandStep {
55 name: Some("Restore cache".to_string()),
56 run: "restore cache".to_string(),
57 ..Default::default()
58 }),
59 post: Some(UserStep::Command(UserCommandStep {
60 name: Some("Save cache".to_string()),
61 run: "save cache".to_string(),
62 ..Default::default()
63 })),
64 })
65 }
66 }
67
68 let mut actions = HashMap::new();
69
70 actions.insert(
71 "caches".to_string(),
72 Box::new(CacheAction {}) as Box<dyn Action>,
73 );
74
75 let actions = ActionDriver::new(actions);
76
77 let test_step = UserActionStep {
78 uses: "caches".to_string(),
79 ..Default::default()
80 };
81
82 let steps = actions.try_normalize(test_step)?.unwrap();
83
84 assert!(steps.pre.is_none());
85
86 if let UserStep::Command(step) = steps.run {
87 assert_eq!(step.name, Some("Restore cache".to_string()));
88 assert_eq!(step.run, "restore cache".to_string());
89 } else {
90 panic!("Should be command step");
91 }
92
93 if let Some(UserStep::Command(step)) = steps.post {
94 assert_eq!(step.name, Some("Save cache".to_string()));
95 assert_eq!(step.run, "save cache".to_string());
96 } else {
97 panic!("Should be command step");
98 }
99
100 Ok(())
101 }
102
103 #[test]
104 fn test_not_exists_action() -> Result<()> {
105 let actions = HashMap::new();
106
107 let actions = ActionDriver::new(actions);
108
109 let step = UserActionStep {
110 uses: "not-exists-action".to_string(),
111 ..Default::default()
112 };
113
114 let result = actions.try_normalize(step).unwrap();
115
116 assert!(result.is_none(),);
117
118 Ok(())
119 }
120}