1
use serde::Deserialize;
2

            
3
use crate::defaults::{
4
  default_ignore_errors,
5
  default_verbose,
6
};
7
use crate::schema::TaskContext;
8

            
9
#[derive(Debug, Deserialize)]
10
pub struct TaskRun {
11
  /// The name of the task to run
12
  pub task: String,
13

            
14
  /// Ignore errors if the task commands fail
15
  #[serde(default)]
16
  pub ignore_errors: Option<bool>,
17

            
18
  /// Show verbose output
19
  #[serde(default)]
20
  pub verbose: Option<bool>,
21
}
22

            
23
impl TaskRun {
24
  pub fn execute(&self, context: &TaskContext) -> anyhow::Result<()> {
25
    assert!(!self.task.is_empty());
26

            
27
    let ignore_errors = self.ignore_errors(context);
28
    let verbose = self.verbose(context);
29

            
30
    let task = context
31
      .task_root
32
      .tasks
33
      .get(&self.task)
34
      .ok_or_else(|| anyhow::anyhow!("Task not found"))?;
35

            
36
    log::trace!("Task: {:?}", task);
37

            
38
    {
39
      let mut stack = context
40
        .execution_stack
41
        .lock()
42
        .map_err(|e| anyhow::anyhow!("Failed to lock execution stack - {}", e))?;
43

            
44
      if stack.contains(&self.task) {
45
        anyhow::bail!("Circular dependency detected - {}", &self.task);
46
      }
47

            
48
      stack.insert(self.task.clone());
49
    }
50

            
51
    let mut context = TaskContext::from_context_with_args(context, ignore_errors, verbose);
52
    task.run(&mut context)?;
53

            
54
    Ok(())
55
  }
56

            
57
  fn ignore_errors(&self, context: &TaskContext) -> bool {
58
    self
59
      .ignore_errors
60
      .or(context.ignore_errors)
61
      .unwrap_or(default_ignore_errors())
62
  }
63

            
64
  fn verbose(&self, context: &TaskContext) -> bool {
65
    self.verbose.or(context.verbose).unwrap_or(default_verbose())
66
  }
67
}