Skip to main content

treeboot_core/
status.rs

1use std::path::PathBuf;
2
3use serde::Serialize;
4
5use crate::check::WorktreeSnapshot;
6use crate::context;
7use crate::{Config, IgnoredInitScript, InitScriptDiscovery, Result, Worktree, WorktreeOptions};
8
9/// Options for inspecting treeboot discovery status.
10#[derive(Debug, Clone, Default, PartialEq, Eq)]
11pub struct StatusOptions {
12    /// Directory from which status discovery starts.
13    pub cwd: Option<PathBuf>,
14    /// Overrides the root checkout used for discovery.
15    pub root: Option<PathBuf>,
16    /// Uses one specific config file instead of config discovery.
17    pub config: Option<PathBuf>,
18    /// Skips init script discovery.
19    pub no_init_script: bool,
20}
21
22/// Init script discovery status for a worktree.
23#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
24#[serde(tag = "status", rename_all = "snake_case")]
25pub enum InitScriptStatus {
26    /// Init script discovery was skipped by options.
27    Skipped,
28    /// No executable init script was found.
29    NotFound {
30        /// Existing init script paths that were ignored.
31        ignored: Vec<IgnoredInitScript>,
32    },
33    /// An executable init script was found.
34    Found {
35        /// Init script path.
36        path: PathBuf,
37    },
38}
39
40/// Result summary for a `treeboot status` invocation.
41#[derive(Debug, Clone, PartialEq, Eq)]
42pub struct StatusReport {
43    /// Runtime context discovered for the current worktree.
44    pub context: Worktree,
45    /// Init script discovery result.
46    pub init_script: InitScriptStatus,
47    /// Selected config path, when one was requested or discovered.
48    pub config: Option<PathBuf>,
49}
50
51/// Serializable result summary for a `treeboot status` invocation.
52#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
53pub struct StatusSnapshotReport {
54    /// Runtime context snapshot discovered for the current worktree.
55    pub context: WorktreeSnapshot,
56    /// Init script discovery result.
57    pub init_script: InitScriptStatus,
58    /// Selected config path, when one was requested or discovered.
59    pub config: Option<PathBuf>,
60}
61
62impl From<&StatusReport> for StatusSnapshotReport {
63    fn from(report: &StatusReport) -> Self {
64        Self {
65            context: WorktreeSnapshot::from(&report.context),
66            init_script: report.init_script.clone(),
67            config: report.config.clone(),
68        }
69    }
70}
71
72/// Inspects worktree, root, init script, and config discovery status.
73///
74/// This function does not execute init scripts, parse config, or run configured
75/// commands.
76///
77/// # Errors
78///
79/// Returns an error if context discovery fails or a requested config file does
80/// not exist.
81pub fn inspect_status(options: StatusOptions) -> Result<StatusReport> {
82    let context = context::resolve(&WorktreeOptions {
83        cwd: options.cwd,
84        root: options.root,
85    })?;
86    let init_script = if options.no_init_script || options.config.is_some() {
87        InitScriptStatus::Skipped
88    } else {
89        inspect_init_script(&context)
90    };
91    let config = Config::discover_path(&context, options.config.as_deref())?;
92
93    Ok(StatusReport {
94        context,
95        init_script,
96        config,
97    })
98}
99
100/// Inspects worktree, root, init script, and config discovery status as a
101/// serializable snapshot.
102///
103/// This function does not execute init scripts, parse config, or run configured
104/// commands.
105///
106/// # Errors
107///
108/// Returns an error if context discovery fails or a requested config file does
109/// not exist.
110pub fn inspect_status_snapshot(options: StatusOptions) -> Result<StatusSnapshotReport> {
111    inspect_status(options).map(|report| StatusSnapshotReport::from(&report))
112}
113
114fn inspect_init_script(context: &Worktree) -> InitScriptStatus {
115    let scripts = InitScriptDiscovery::discover(context);
116
117    if let Some(path) = scripts.executable {
118        InitScriptStatus::Found { path }
119    } else {
120        InitScriptStatus::NotFound {
121            ignored: scripts.ignored,
122        }
123    }
124}