Skip to main content

bucketwarden_browser_ui_preferences/
lib.rs

1use serde::{Deserialize, Serialize};
2
3pub const CRATE_PURPOSE: &str = "browser ui preferences contracts";
4pub const PREFERENCES_BOUNDARY_ID: &str = "bnd:bucketwarden.browser-ui.preferences-slice";
5
6#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
7pub struct BrowserUiPreferenceFeature {
8    pub feature_id: String,
9    pub title: String,
10    pub runtime_surface: String,
11    pub api_endpoint: String,
12}
13
14#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
15pub struct BrowserUiPreferencesContract {
16    pub boundary_id: String,
17    pub implementation_status: String,
18    pub features: Vec<BrowserUiPreferenceFeature>,
19    pub preference_keys: Vec<String>,
20    pub persistence_controls: Vec<String>,
21    pub validation_rules: Vec<String>,
22    pub failure_states: Vec<String>,
23}
24
25pub fn browser_ui_preferences_contract() -> BrowserUiPreferencesContract {
26    BrowserUiPreferencesContract {
27        boundary_id: PREFERENCES_BOUNDARY_ID.to_string(),
28        implementation_status: "implemented".to_string(),
29        features: browser_ui_preference_features(),
30        preference_keys: browser_ui_preference_keys(),
31        persistence_controls: browser_ui_preference_persistence_controls(),
32        validation_rules: browser_ui_preference_validation_rules(),
33        failure_states: browser_ui_preference_failure_states(),
34    }
35}
36
37pub fn browser_ui_preference_features() -> Vec<BrowserUiPreferenceFeature> {
38    [
39        (
40            "feat:bucketwarden.ui.browser.preferences-view",
41            "Browser UI preferences view",
42            "browser-ui.preferences.view",
43            "/ui/api/preferences",
44        ),
45        (
46            "feat:bucketwarden.ui.browser.preferences-persistence",
47            "Browser UI preferences persistence",
48            "browser-ui.preferences.persistence",
49            "/ui/api/preferences",
50        ),
51    ]
52    .into_iter()
53    .map(
54        |(feature_id, title, runtime_surface, api_endpoint)| BrowserUiPreferenceFeature {
55            feature_id: feature_id.to_string(),
56            title: title.to_string(),
57            runtime_surface: runtime_surface.to_string(),
58            api_endpoint: api_endpoint.to_string(),
59        },
60    )
61    .collect()
62}
63
64pub fn browser_ui_preference_keys() -> Vec<String> {
65    [
66        "bucketwarden.ui.density",
67        "bucketwarden.ui.refreshSeconds",
68        "bucketwarden.ui.visibleColumns",
69        "bucketwarden.ui.reportFilters",
70    ]
71    .into_iter()
72    .map(str::to_string)
73    .collect()
74}
75
76pub fn browser_ui_preference_persistence_controls() -> Vec<String> {
77    [
78        "authenticated preferences read",
79        "authenticated preferences write",
80        "runtime snapshot restore",
81        "principal-scoped preference map",
82        "audit event on write",
83    ]
84    .into_iter()
85    .map(str::to_string)
86    .collect()
87}
88
89pub fn browser_ui_preference_validation_rules() -> Vec<String> {
90    [
91        "maximum 64 preference keys",
92        "non-empty key",
93        "key length <= 128",
94        "value length <= 4096",
95        "ASCII alphanumeric dot dash underscore keys",
96    ]
97    .into_iter()
98    .map(str::to_string)
99    .collect()
100}
101
102pub fn browser_ui_preference_failure_states() -> Vec<String> {
103    [
104        "unauthenticated preferences request",
105        "invalid preference key",
106        "oversized preference value",
107        "too many preference keys",
108        "preferences save failure",
109    ]
110    .into_iter()
111    .map(str::to_string)
112    .collect()
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118
119    #[test]
120    fn preferences_contract_tracks_all_slice_features() {
121        let contract = browser_ui_preferences_contract();
122        let feature_ids = contract
123            .features
124            .iter()
125            .map(|feature| feature.feature_id.as_str())
126            .collect::<Vec<_>>();
127
128        assert_eq!(contract.boundary_id, PREFERENCES_BOUNDARY_ID);
129        assert_eq!(contract.implementation_status, "implemented");
130        assert!(feature_ids.contains(&"feat:bucketwarden.ui.browser.preferences-view"));
131        assert!(feature_ids.contains(&"feat:bucketwarden.ui.browser.preferences-persistence"));
132    }
133
134    #[test]
135    fn preferences_contract_requires_persistence_and_validation() {
136        let contract = browser_ui_preferences_contract();
137
138        assert!(contract
139            .preference_keys
140            .contains(&"bucketwarden.ui.refreshSeconds".to_string()));
141        assert!(contract
142            .persistence_controls
143            .contains(&"runtime snapshot restore".to_string()));
144        assert!(contract
145            .validation_rules
146            .contains(&"ASCII alphanumeric dot dash underscore keys".to_string()));
147        assert!(contract
148            .failure_states
149            .contains(&"unauthenticated preferences request".to_string()));
150    }
151}