1use std::fmt;
4
5use ito_config::types::{CoordinationStorage, ItoConfig};
6
7use crate::errors::{CoreError, CoreResult};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum CompiledFeature {
12 Backend,
14 CoordinationBranch,
16}
17
18impl CompiledFeature {
19 pub const fn as_str(self) -> &'static str {
21 match self {
22 Self::Backend => "backend",
23 Self::CoordinationBranch => "coordination-branch",
24 }
25 }
26
27 pub const fn is_compiled(self) -> bool {
32 match self {
33 Self::Backend => cfg!(feature = "backend"),
34 Self::CoordinationBranch => cfg!(feature = "coordination-branch"),
35 }
36 }
37}
38
39impl fmt::Display for CompiledFeature {
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41 f.write_str(self.as_str())
42 }
43}
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
47pub struct CompiledCapabilities {
48 pub backend: bool,
50 pub coordination_branch: bool,
52}
53
54impl CompiledCapabilities {
55 pub const fn current() -> Self {
57 Self {
58 backend: cfg!(feature = "backend"),
59 coordination_branch: cfg!(feature = "coordination-branch"),
60 }
61 }
62
63 pub const fn contains(self, feature: CompiledFeature) -> bool {
65 match feature {
66 CompiledFeature::Backend => self.backend,
67 CompiledFeature::CoordinationBranch => self.coordination_branch,
68 }
69 }
70}
71
72#[derive(Debug, Clone, Copy, PartialEq, Eq)]
74pub enum CapabilityPreflight {
75 Stateful,
77 Recovery,
79}
80
81pub fn preflight_config(config: &ItoConfig, mode: CapabilityPreflight) -> CoreResult<()> {
87 preflight_config_with_capabilities(config, mode, CompiledCapabilities::current())
88}
89
90pub fn preflight_config_with_capabilities(
97 config: &ItoConfig,
98 mode: CapabilityPreflight,
99 capabilities: CompiledCapabilities,
100) -> CoreResult<()> {
101 if mode == CapabilityPreflight::Recovery {
102 return Ok(());
103 }
104
105 if config.backend.enabled && !capabilities.contains(CompiledFeature::Backend) {
106 return Err(CoreError::feature_unavailable(
107 CompiledFeature::Backend,
108 "backend.enabled",
109 "disable backend.enabled, or install an experimental build with the backend feature",
110 ));
111 }
112
113 let coordination = &config.changes.coordination_branch;
114 if (coordination.enabled.0 || coordination.storage == CoordinationStorage::Worktree)
115 && !capabilities.contains(CompiledFeature::CoordinationBranch)
116 {
117 let requested_by = if coordination.enabled.0 {
118 "changes.coordination_branch.enabled"
119 } else {
120 "changes.coordination_branch.storage=worktree"
121 };
122 return Err(CoreError::feature_unavailable(
123 CompiledFeature::CoordinationBranch,
124 requested_by,
125 "run `ito agent instruction migrate-to-main`, then disable coordination-branch synchronization",
126 ));
127 }
128
129 Ok(())
130}
131
132#[cfg(test)]
133#[path = "capabilities_tests.rs"]
134mod capabilities_tests;