Skip to main content

treeboot_core/
executor.rs

1use crate::commands::{CommandExecutionOptions, execute_commands};
2use crate::files::{FileApplyOptions, apply_file_operations};
3use crate::{ActionPlan, Reporter, Result};
4
5/// Options that control action plan execution.
6#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
7pub struct ExecuteOptions {
8    /// Rejects strict-mode file-operation conflicts.
9    pub strict: bool,
10    /// Replaces existing file-operation targets where supported.
11    pub force: bool,
12    /// Prints planned work without changing files or running commands.
13    pub dry_run: bool,
14    /// Applies file operations only.
15    pub skip_commands: bool,
16}
17
18/// Result summary for action plan execution.
19#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
20pub struct ExecutionReport {
21    /// Number of file actions applied or reported.
22    pub file_action_count: usize,
23}
24
25/// Executes validated action plans.
26#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
27pub struct Executor {
28    options: ExecuteOptions,
29}
30
31impl Executor {
32    /// Creates an executor from execution options.
33    #[must_use]
34    pub const fn new(options: ExecuteOptions) -> Self {
35        Self { options }
36    }
37
38    /// Applies the file-operation portion of a plan.
39    ///
40    /// # Errors
41    ///
42    /// Returns an error if file operation application or output reporting
43    /// fails.
44    pub fn execute_files(
45        &self,
46        plan: &ActionPlan,
47        reporter: &mut dyn Reporter,
48    ) -> Result<ExecutionReport> {
49        let report = apply_file_operations(
50            plan,
51            FileApplyOptions {
52                strict: self.options.strict,
53                force: self.options.force,
54                dry_run: self.options.dry_run,
55            },
56            reporter,
57        )?;
58
59        Ok(ExecutionReport {
60            file_action_count: report.action_count,
61        })
62    }
63
64    /// Executes the command portion of a plan.
65    ///
66    /// # Errors
67    ///
68    /// Returns an error if command execution or output reporting fails.
69    pub fn execute_commands(&self, plan: &ActionPlan, reporter: &mut dyn Reporter) -> Result<()> {
70        execute_commands(
71            plan,
72            CommandExecutionOptions {
73                dry_run: self.options.dry_run,
74            },
75            reporter,
76        )
77    }
78
79    /// Executes a complete action plan.
80    ///
81    /// # Errors
82    ///
83    /// Returns an error if file operation application, command execution, or
84    /// output reporting fails.
85    pub fn execute(
86        &self,
87        plan: &ActionPlan,
88        reporter: &mut dyn Reporter,
89    ) -> Result<ExecutionReport> {
90        let report = self.execute_files(plan, reporter)?;
91
92        if !self.options.skip_commands {
93            self.execute_commands(plan, reporter)?;
94        }
95
96        Ok(report)
97    }
98}