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}