Skip to main content

sqry_plugin_registry/
feature_table.rs

1//! Compile-time mapping from plugin id → Cargo feature flag, used to
2//! turn the otherwise-opaque `IncompatibleUnknownPluginIds` error
3//! (cluster-E §E.2) into an actionable diagnostic that names the
4//! `--features` list to pass to `cargo install`.
5//!
6//! The set of feature-gated plugins is the fixed list under
7//! `[features]` in `sqry-plugin-registry/Cargo.toml` (apex / abap /
8//! servicenow-xanadu-js / servicenow-xml / terraform / puppet / pulumi).
9//! `enabled_in_this_binary` is decided by `cfg!(feature = …)` so the
10//! runtime cost is zero and the answer matches the binary the user is
11//! actually running.
12
13/// One entry in the plugin-id → cargo-feature mapping.
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub struct PluginFeatureSpec {
16    /// Plugin id as it appears in the persisted manifest (matches
17    /// `BUILTIN_PLUGIN_SPECS[*].id`).
18    pub plugin_id: &'static str,
19    /// Cargo feature flag in `sqry-plugin-registry/Cargo.toml` that
20    /// pulls the plugin's optional crate dependency in.
21    pub feature_flag: &'static str,
22    /// `true` iff the current binary was compiled with `feature_flag`
23    /// enabled. Computed at compile time via `cfg!(feature = …)`.
24    pub enabled_in_this_binary: bool,
25}
26
27/// Authoritative table of feature-gated plugins. Keep in sync with
28/// `sqry-plugin-registry/Cargo.toml::[features]` (the `plugin-*`
29/// entries) — failing that, `missing_features_for` will silently
30/// return an empty list for the unknown ids and the user-visible
31/// error degrades to "rebuild the index".
32pub const PLUGIN_FEATURE_TABLE: &[PluginFeatureSpec] = &[
33    PluginFeatureSpec {
34        plugin_id: "apex",
35        feature_flag: "plugin-apex",
36        enabled_in_this_binary: cfg!(feature = "plugin-apex"),
37    },
38    PluginFeatureSpec {
39        plugin_id: "abap",
40        feature_flag: "plugin-abap",
41        enabled_in_this_binary: cfg!(feature = "plugin-abap"),
42    },
43    PluginFeatureSpec {
44        plugin_id: "servicenow-xanadu-js",
45        feature_flag: "plugin-servicenow-xanadu",
46        enabled_in_this_binary: cfg!(feature = "plugin-servicenow-xanadu"),
47    },
48    PluginFeatureSpec {
49        plugin_id: "servicenow-xml",
50        feature_flag: "plugin-servicenow-xml",
51        enabled_in_this_binary: cfg!(feature = "plugin-servicenow-xml"),
52    },
53    PluginFeatureSpec {
54        plugin_id: "terraform",
55        feature_flag: "plugin-terraform",
56        enabled_in_this_binary: cfg!(feature = "plugin-terraform"),
57    },
58    PluginFeatureSpec {
59        plugin_id: "puppet",
60        feature_flag: "plugin-puppet",
61        enabled_in_this_binary: cfg!(feature = "plugin-puppet"),
62    },
63    PluginFeatureSpec {
64        plugin_id: "pulumi",
65        feature_flag: "plugin-pulumi",
66        enabled_in_this_binary: cfg!(feature = "plugin-pulumi"),
67    },
68];
69
70/// Sort + dedup the feature flags that *would* register the unknown
71/// plugin ids if enabled at build time. Returns an empty list when
72/// none of the unknown ids correspond to a known feature gate (i.e.
73/// the manifest is from a strictly newer sqry, or the ids are typos).
74#[must_use]
75pub fn missing_features_for<S: AsRef<str>>(unknown_ids: &[S]) -> Vec<&'static str> {
76    let mut out: Vec<&'static str> = unknown_ids
77        .iter()
78        .filter_map(|id| {
79            PLUGIN_FEATURE_TABLE
80                .iter()
81                .find(|spec| spec.plugin_id == id.as_ref() && !spec.enabled_in_this_binary)
82                .map(|spec| spec.feature_flag)
83        })
84        .collect();
85    out.sort_unstable();
86    out.dedup();
87    out
88}
89
90/// `true` when every entry in `unknown_ids` corresponds to a known
91/// feature flag. Drives the "rebuild this binary with `--features …`"
92/// vs the "rebuild the index" suggestion in user-facing errors.
93#[must_use]
94pub fn all_unknown_ids_have_features<S: AsRef<str>>(unknown_ids: &[S]) -> bool {
95    !unknown_ids.is_empty()
96        && unknown_ids.iter().all(|id| {
97            PLUGIN_FEATURE_TABLE
98                .iter()
99                .any(|spec| spec.plugin_id == id.as_ref())
100        })
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106
107    /// Every entry in the table names a feature flag declared in
108    /// `Cargo.toml`. Verified indirectly by the fact that the file
109    /// compiles — `cfg!(feature = "…")` is a no-op for a non-existent
110    /// flag, but a typo here would silently disable the suggestion
111    /// without a build error. We therefore pin the exact set so a
112    /// rename of a feature flag in `Cargo.toml` forces a code
113    /// review here.
114    #[test]
115    fn feature_table_contains_every_known_specialty_plugin() {
116        let ids: Vec<&'static str> = PLUGIN_FEATURE_TABLE.iter().map(|s| s.plugin_id).collect();
117        assert_eq!(
118            ids,
119            [
120                "apex",
121                "abap",
122                "servicenow-xanadu-js",
123                "servicenow-xml",
124                "terraform",
125                "puppet",
126                "pulumi",
127            ]
128        );
129    }
130
131    /// Unknown ids that match a (disabled) feature gate yield the gate.
132    #[test]
133    fn missing_features_for_returns_disabled_gates() {
134        // At least one of the specialty plugins is guaranteed disabled
135        // unless the test runs with `--features specialty-plugins`.
136        let result = missing_features_for(&["terraform"]);
137        if !cfg!(feature = "plugin-terraform") {
138            assert_eq!(result, vec!["plugin-terraform"]);
139        }
140    }
141
142    /// Unknown ids that match no feature gate yield an empty list.
143    #[test]
144    fn missing_features_for_returns_empty_for_unrelated_ids() {
145        let result = missing_features_for(&["totally-made-up-plugin-id"]);
146        assert!(result.is_empty(), "expected empty, got {result:?}");
147    }
148
149    /// `all_unknown_ids_have_features` is `true` iff every id matches
150    /// a feature gate.
151    #[test]
152    fn all_have_features_only_when_every_id_matches() {
153        assert!(all_unknown_ids_have_features(&["terraform"]));
154        assert!(all_unknown_ids_have_features(&["terraform", "puppet"]));
155        assert!(!all_unknown_ids_have_features(&["terraform", "made-up"]));
156        assert!(!all_unknown_ids_have_features::<&str>(&[]));
157    }
158}