Skip to main content

verbs/
context.rs

1// SPDX-License-Identifier: Apache-2.0
2//! Execution context shared by future facade operations.
3
4use std::path::PathBuf;
5
6use objects::HeddleError;
7use repo::{FsMonitorMode, Repository, WorktreeStatusOptions};
8
9/// Semantic execution state for embeddable Heddle operations.
10///
11/// Carries only resolved values: the caller (usually the CLI) resolves user
12/// config before construction, so this crate never reads config files.
13pub struct ExecutionContext {
14    repo: Option<Repository>,
15    start_path: Option<PathBuf>,
16    principal_fallback: Option<(String, String)>,
17    fsmonitor_mode: FsMonitorMode,
18}
19
20impl ExecutionContext {
21    pub fn builder() -> ExecutionContextBuilder {
22        ExecutionContextBuilder::default()
23    }
24
25    pub fn require_repo(&self) -> Result<&Repository, HeddleError> {
26        self.repo
27            .as_ref()
28            .ok_or_else(|| HeddleError::RepositoryNotFound(PathBuf::from(".")))
29    }
30
31    pub fn repo(&self) -> Option<&Repository> {
32        self.repo.as_ref()
33    }
34
35    pub fn start_path(&self) -> Option<&std::path::Path> {
36        self.start_path.as_deref()
37    }
38
39    /// User-config principal fallback as an optional `(name, email)` pair.
40    pub fn principal_fallback(&self) -> Option<(&str, &str)> {
41        self.principal_fallback
42            .as_ref()
43            .map(|(name, email)| (name.as_str(), email.as_str()))
44    }
45
46    /// Resolved fsmonitor mode for worktree-status hot paths.
47    pub fn fsmonitor_mode(&self) -> FsMonitorMode {
48        self.fsmonitor_mode
49    }
50
51    pub fn worktree_status_options(&self) -> WorktreeStatusOptions {
52        WorktreeStatusOptions {
53            fsmonitor: repo::FsMonitorSettings {
54                mode: self.fsmonitor_mode,
55            },
56        }
57    }
58}
59
60/// Builder for [`ExecutionContext`].
61#[derive(Default)]
62pub struct ExecutionContextBuilder {
63    repo: Option<Repository>,
64    start_path: Option<PathBuf>,
65    principal_fallback: Option<(String, String)>,
66    fsmonitor_mode: FsMonitorMode,
67}
68
69impl ExecutionContextBuilder {
70    pub fn repo(mut self, repo: Repository) -> Self {
71        self.repo = Some(repo);
72        self
73    }
74
75    pub fn start_path(mut self, path: impl Into<PathBuf>) -> Self {
76        self.start_path = Some(path.into());
77        self
78    }
79
80    pub fn principal_fallback(mut self, principal: Option<(String, String)>) -> Self {
81        self.principal_fallback = principal;
82        self
83    }
84
85    pub fn fsmonitor_mode(mut self, mode: FsMonitorMode) -> Self {
86        self.fsmonitor_mode = mode;
87        self
88    }
89
90    pub fn build(self) -> ExecutionContext {
91        ExecutionContext {
92            repo: self.repo,
93            start_path: self.start_path,
94            principal_fallback: self.principal_fallback,
95            fsmonitor_mode: self.fsmonitor_mode,
96        }
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    use super::*;
103
104    #[test]
105    fn default_context_has_no_repo_and_noop_sinks() {
106        let ctx = ExecutionContext::builder().build();
107
108        assert!(matches!(
109            ctx.require_repo(),
110            Err(HeddleError::RepositoryNotFound(_))
111        ));
112        assert_eq!(ctx.fsmonitor_mode(), FsMonitorMode::Off);
113        assert!(ctx.principal_fallback().is_none());
114    }
115
116    #[test]
117    fn builder_sets_non_repo_fields() {
118        let ctx = ExecutionContext::builder()
119            .start_path("/tmp/heddle-verbs-context-test")
120            .principal_fallback(Some(("Luke".into(), "luke@example.com".into())))
121            .fsmonitor_mode(FsMonitorMode::Watchman)
122            .build();
123
124        assert_eq!(
125            ctx.start_path(),
126            Some(std::path::Path::new("/tmp/heddle-verbs-context-test"))
127        );
128        assert_eq!(ctx.principal_fallback(), Some(("Luke", "luke@example.com")));
129        assert_eq!(
130            ctx.worktree_status_options().fsmonitor.mode,
131            FsMonitorMode::Watchman
132        );
133    }
134}