Skip to main content

verbs/
onboarding.rs

1// SPDX-License-Identifier: Apache-2.0
2//! Pure repository-onboarding classification.
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum OnboardingRepositoryState {
6    NativeUninitialized,
7    PlainGitUnborn,
8    PlainGitCommitted,
9    NativeInitialized,
10    GitOverlayInitialized,
11}
12
13impl OnboardingRepositoryState {
14    pub const fn as_str(self) -> &'static str {
15        match self {
16            Self::NativeUninitialized => "native_uninitialized",
17            Self::PlainGitUnborn => "plain_git_unborn",
18            Self::PlainGitCommitted => "plain_git_committed",
19            Self::NativeInitialized => "native_initialized",
20            Self::GitOverlayInitialized => "git_overlay_initialized",
21        }
22    }
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub enum OnboardingMode {
27    Native,
28    GitOverlay,
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub enum OnboardingAction {
33    Init,
34    None,
35}
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38pub struct OnboardingFacts {
39    pub git_worktree: bool,
40    pub git_has_commits: bool,
41    pub heddle_mode: Option<OnboardingMode>,
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45pub struct OnboardingPlan {
46    pub state: OnboardingRepositoryState,
47    pub mode: OnboardingMode,
48    pub action: OnboardingAction,
49}
50
51impl OnboardingPlan {
52    pub fn recommended_command(self) -> Option<&'static str> {
53        match self.action {
54            OnboardingAction::Init => Some("heddle init"),
55            OnboardingAction::None => None,
56        }
57    }
58
59    pub fn storage_summary(self) -> &'static str {
60        match self.mode {
61            OnboardingMode::Native => "Heddle owns source objects, refs, and worktree state",
62            OnboardingMode::GitOverlay => {
63                "Git owns source objects, refs, index, and worktree state; Heddle owns metadata, provenance, and projection mapping"
64            }
65        }
66    }
67}
68
69pub fn plan_repository_onboarding(facts: OnboardingFacts) -> OnboardingPlan {
70    if let Some(mode) = facts.heddle_mode {
71        let state = match mode {
72            OnboardingMode::Native => OnboardingRepositoryState::NativeInitialized,
73            OnboardingMode::GitOverlay => OnboardingRepositoryState::GitOverlayInitialized,
74        };
75        return OnboardingPlan {
76            state,
77            mode,
78            action: OnboardingAction::None,
79        };
80    }
81
82    if facts.git_worktree {
83        return OnboardingPlan {
84            state: if facts.git_has_commits {
85                OnboardingRepositoryState::PlainGitCommitted
86            } else {
87                OnboardingRepositoryState::PlainGitUnborn
88            },
89            mode: OnboardingMode::GitOverlay,
90            action: OnboardingAction::Init,
91        };
92    }
93
94    OnboardingPlan {
95        state: OnboardingRepositoryState::NativeUninitialized,
96        mode: OnboardingMode::Native,
97        action: OnboardingAction::Init,
98    }
99}
100
101#[cfg(test)]
102mod tests {
103    use super::*;
104
105    #[test]
106    fn classifies_uninitialized_repositories() {
107        let committed = plan_repository_onboarding(OnboardingFacts {
108            git_worktree: true,
109            git_has_commits: true,
110            heddle_mode: None,
111        });
112        assert_eq!(
113            committed.state,
114            OnboardingRepositoryState::PlainGitCommitted
115        );
116        assert_eq!(committed.mode, OnboardingMode::GitOverlay);
117        assert_eq!(committed.recommended_command(), Some("heddle init"));
118
119        let unborn = plan_repository_onboarding(OnboardingFacts {
120            git_worktree: true,
121            git_has_commits: false,
122            heddle_mode: None,
123        });
124        assert_eq!(unborn.state, OnboardingRepositoryState::PlainGitUnborn);
125        assert_eq!(unborn.mode, OnboardingMode::GitOverlay);
126        assert_eq!(unborn.recommended_command(), Some("heddle init"));
127
128        let native = plan_repository_onboarding(OnboardingFacts {
129            git_worktree: false,
130            git_has_commits: false,
131            heddle_mode: None,
132        });
133        assert_eq!(native.state, OnboardingRepositoryState::NativeUninitialized);
134        assert_eq!(native.mode, OnboardingMode::Native);
135        assert_eq!(native.recommended_command(), Some("heddle init"));
136    }
137
138    #[test]
139    fn initialized_overlay_needs_no_onboarding_action() {
140        let plan = plan_repository_onboarding(OnboardingFacts {
141            git_worktree: true,
142            git_has_commits: true,
143            heddle_mode: Some(OnboardingMode::GitOverlay),
144        });
145        assert_eq!(plan.state, OnboardingRepositoryState::GitOverlayInitialized);
146        assert_eq!(plan.action, OnboardingAction::None);
147        assert_eq!(plan.recommended_command(), None);
148    }
149}