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