Skip to main content

lexicon_spec/
capability.rs

1//! Capability model for progressive adoption.
2
3use std::collections::HashSet;
4
5use serde::{Deserialize, Serialize};
6
7use crate::mode::OperatingMode;
8
9/// A discrete capability that may be enabled depending on the operating mode.
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
11#[serde(rename_all = "snake_case")]
12pub enum Capability {
13    // --- Repo-level ---
14    RepoContracts,
15    RepoConformance,
16    RepoScoring,
17    RepoGates,
18    RepoApi,
19
20    // --- Workspace-level ---
21    WorkspaceArchitecture,
22    WorkspaceDependencyLaw,
23    WorkspaceSharedContracts,
24
25    // --- Ecosystem-level ---
26    EcosystemGovernance,
27    EcosystemSharedContracts,
28    EcosystemImpact,
29}
30
31/// A set of capabilities enabled for a given operating mode.
32#[derive(Debug, Clone, PartialEq, Eq)]
33pub struct CapabilitySet {
34    inner: HashSet<Capability>,
35}
36
37impl CapabilitySet {
38    /// Build the capability set for the given mode.
39    pub fn for_mode(mode: OperatingMode) -> Self {
40        let mut caps = HashSet::new();
41
42        // Repo capabilities are always present.
43        caps.insert(Capability::RepoContracts);
44        caps.insert(Capability::RepoConformance);
45        caps.insert(Capability::RepoScoring);
46        caps.insert(Capability::RepoGates);
47        caps.insert(Capability::RepoApi);
48
49        if matches!(mode, OperatingMode::Workspace | OperatingMode::Ecosystem) {
50            caps.insert(Capability::WorkspaceArchitecture);
51            caps.insert(Capability::WorkspaceDependencyLaw);
52            caps.insert(Capability::WorkspaceSharedContracts);
53        }
54
55        if mode == OperatingMode::Ecosystem {
56            caps.insert(Capability::EcosystemGovernance);
57            caps.insert(Capability::EcosystemSharedContracts);
58            caps.insert(Capability::EcosystemImpact);
59        }
60
61        Self { inner: caps }
62    }
63
64    /// Check whether the set contains a capability.
65    pub fn has(&self, cap: Capability) -> bool {
66        self.inner.contains(&cap)
67    }
68
69    /// Returns true if no capabilities are enabled.
70    pub fn is_empty(&self) -> bool {
71        self.inner.is_empty()
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78
79    #[test]
80    fn test_repo_capabilities() {
81        let set = CapabilitySet::for_mode(OperatingMode::Repo);
82        assert_eq!(set.inner.len(), 5);
83        assert!(set.has(Capability::RepoContracts));
84        assert!(set.has(Capability::RepoConformance));
85        assert!(set.has(Capability::RepoScoring));
86        assert!(set.has(Capability::RepoGates));
87        assert!(set.has(Capability::RepoApi));
88        assert!(!set.has(Capability::WorkspaceArchitecture));
89        assert!(!set.has(Capability::EcosystemGovernance));
90    }
91
92    #[test]
93    fn test_workspace_capabilities() {
94        let set = CapabilitySet::for_mode(OperatingMode::Workspace);
95        assert_eq!(set.inner.len(), 8);
96        // Has all repo caps
97        assert!(set.has(Capability::RepoContracts));
98        assert!(set.has(Capability::RepoApi));
99        // Plus workspace caps
100        assert!(set.has(Capability::WorkspaceArchitecture));
101        assert!(set.has(Capability::WorkspaceDependencyLaw));
102        assert!(set.has(Capability::WorkspaceSharedContracts));
103        // But not ecosystem
104        assert!(!set.has(Capability::EcosystemGovernance));
105    }
106
107    #[test]
108    fn test_ecosystem_capabilities() {
109        let set = CapabilitySet::for_mode(OperatingMode::Ecosystem);
110        assert_eq!(set.inner.len(), 11);
111        assert!(set.has(Capability::RepoContracts));
112        assert!(set.has(Capability::WorkspaceArchitecture));
113        assert!(set.has(Capability::EcosystemGovernance));
114        assert!(set.has(Capability::EcosystemSharedContracts));
115        assert!(set.has(Capability::EcosystemImpact));
116    }
117
118    #[test]
119    fn test_is_empty() {
120        let set = CapabilitySet::for_mode(OperatingMode::Repo);
121        assert!(!set.is_empty());
122    }
123
124    #[test]
125    fn test_capability_serde_roundtrip() {
126        let cap = Capability::WorkspaceArchitecture;
127        let json = serde_json::to_string(&cap).unwrap();
128        let back: Capability = serde_json::from_str(&json).unwrap();
129        assert_eq!(cap, back);
130    }
131}