rs_teststand/adapters/adapter_key_name.rs
1//! The keys naming the code-module adapters an engine can call.
2
3/// A code-module adapter, by the key the engine knows it under
4/// (`AdapterKeyNames`).
5///
6/// The key is what [`Engine::new_step`](crate::Engine::new_step) and
7/// [`Step::adapter_key_name`](crate::Step::adapter_key_name) exchange, and it
8/// is a string rather than a number, so this exists to keep those strings in
9/// one checked place instead of spread across call sites.
10///
11/// ```
12/// use rs_teststand::AdapterKeyName;
13///
14/// assert_eq!(AdapterKeyName::LabView.as_str(), "G Flexible VI Adapter");
15/// assert_eq!(
16/// AdapterKeyName::from_key("Sequence Adapter"),
17/// Some(AdapterKeyName::Sequence)
18/// );
19/// ```
20///
21/// A key names an adapter the engine recognizes, not one the station can
22/// necessarily run: calling a `LabVIEW` step needs `LabVIEW` present. Building a
23/// step with the key succeeds either way; only running it does not.
24///
25/// Two keys are **obsolete**, and the documentation says so outright: the
26/// standard-prototype [`Self::LabViewStdPrototype`] and [`Self::CviStdPrototype`] are to be
27/// replaced by [`Self::LabView`] and [`Self::Cvi`]. They are kept here
28/// because engines back to 2016 accept them and old sequence files contain
29/// them, but a step built with one reports back its replacement, so a
30/// comparison of "asked for" against "got" differs for exactly those two, by
31/// design rather than by accident. [`Self::is_obsolete`] and
32/// [`Self::replacement`] make that checkable instead of folklore.
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
34#[non_exhaustive]
35pub enum AdapterKeyName {
36 /// No code module, the step type does its own work
37 /// (`NoneAdapterKeyName`).
38 NoneAdapter,
39 /// The `LabVIEW` adapter (`FlexLVAdapterKeyName`, value
40 /// "G Flexible VI Adapter"). This is what the editor calls the `LabVIEW`
41 /// Adapter today, and the one to reach for.
42 LabView,
43 /// The superseded standard-prototype `LabVIEW` adapter. **Obsolete**, the
44 /// documentation directs callers to [`Self::LabView`], and the engine
45 /// substitutes it. The type library carries this one key under two names,
46 /// `LVAdapterKeyName` and `GAdapterKeyName`, so the crate names it once.
47 LabViewStdPrototype,
48 /// `LabVIEW` NXG (`LabVIEWNXGAdapterKeyName`), its own key for the
49 /// discontinued second-generation product, not a spelling of the above.
50 LabViewNxg,
51 /// A C/CVI function with any prototype (`FlexCVIAdapterKeyName`).
52 Cvi,
53 /// A C/CVI function with the standard prototype. **Obsolete**, the
54 /// documentation directs callers to [`Self::Cvi`]
55 /// (`StdCVIAdapterKeyName`).
56 CviStdPrototype,
57 /// A DLL function with any prototype (`FlexCAdapterKeyName`).
58 DllFlex,
59 /// Another sequence (`SequenceAdapterKeyName`).
60 Sequence,
61 /// An `ActiveX` automation server (`AutomationAdapterKeyName`).
62 Automation,
63 /// A .NET assembly member (`DotNetAdapterKeyname`).
64 DotNet,
65 /// A Python module (`PythonAdapterKeyName`).
66 Python,
67 /// `HTBasic` (`HTBasicAdapterKeyName`).
68 HtBasic,
69}
70
71impl AdapterKeyName {
72 /// The key the engine expects.
73 #[must_use]
74 pub const fn as_str(self) -> &'static str {
75 match self {
76 Self::NoneAdapter => "None Adapter",
77 Self::LabView => "G Flexible VI Adapter",
78 Self::LabViewStdPrototype => "G Std Prototype Adapter",
79 Self::LabViewNxg => "LabVIEW NXG Adapter",
80 Self::Cvi => "C/CVI Flexible Prototype Adapter",
81 Self::CviStdPrototype => "C/CVI Std Prototype Adapter",
82 Self::DllFlex => "DLL Flexible Prototype Adapter",
83 Self::Sequence => "Sequence Adapter",
84 Self::Automation => "Automation Adapter",
85 Self::DotNet => "DotNet Adapter",
86 Self::Python => "Python Adapter",
87 Self::HtBasic => "HTBasic Adapter",
88 }
89 }
90
91 /// Recognizes a key read back from a step.
92 ///
93 /// `None` means the key is not one this build names, a newer engine's
94 /// adapter, or a step that has none, which is information, not an error.
95 #[must_use]
96 pub fn from_key(key: &str) -> Option<Self> {
97 [
98 Self::NoneAdapter,
99 Self::LabViewStdPrototype,
100 Self::LabView,
101 Self::LabViewNxg,
102 Self::CviStdPrototype,
103 Self::Cvi,
104 Self::DllFlex,
105 Self::Sequence,
106 Self::Automation,
107 Self::DotNet,
108 Self::Python,
109 Self::HtBasic,
110 ]
111 .into_iter()
112 .find(|candidate| candidate.as_str() == key)
113 }
114
115 /// Whether the documentation marks this key obsolete.
116 ///
117 /// An obsolete key still works, engines back to 2016 accept it, and old
118 /// sequence files are full of them, but a step built with one reports
119 /// [`replacement`](Self::replacement) back instead.
120 #[must_use]
121 pub const fn is_obsolete(self) -> bool {
122 matches!(self, Self::LabViewStdPrototype | Self::CviStdPrototype)
123 }
124
125 /// The key the documentation directs callers to instead, if any.
126 #[must_use]
127 pub const fn replacement(self) -> Option<Self> {
128 match self {
129 Self::LabViewStdPrototype => Some(Self::LabView),
130 Self::CviStdPrototype => Some(Self::Cvi),
131 _ => None,
132 }
133 }
134}
135
136impl std::fmt::Display for AdapterKeyName {
137 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
138 formatter.write_str(self.as_str())
139 }
140}
141
142#[cfg(test)]
143mod tests {
144 use super::AdapterKeyName;
145
146 #[test]
147 fn every_key_round_trips() {
148 for adapter in [
149 AdapterKeyName::NoneAdapter,
150 AdapterKeyName::LabViewStdPrototype,
151 AdapterKeyName::LabView,
152 AdapterKeyName::LabViewNxg,
153 AdapterKeyName::CviStdPrototype,
154 AdapterKeyName::Cvi,
155 AdapterKeyName::DllFlex,
156 AdapterKeyName::Sequence,
157 AdapterKeyName::Automation,
158 AdapterKeyName::DotNet,
159 AdapterKeyName::Python,
160 AdapterKeyName::HtBasic,
161 ] {
162 assert_eq!(AdapterKeyName::from_key(adapter.as_str()), Some(adapter));
163 }
164 }
165
166 #[test]
167 fn an_unrecognized_key_is_reported_rather_than_guessed() {
168 assert_eq!(AdapterKeyName::from_key("Some Future Adapter"), None);
169 assert_eq!(AdapterKeyName::from_key(""), None);
170 }
171
172 #[test]
173 fn the_obsolete_keys_point_at_their_replacements() {
174 // Only these two: the documentation marks exactly LVAdapterKeyName /
175 // GAdapterKeyName and StdCVIAdapterKeyName obsolete. LabVIEW NXG is a
176 // discontinued product but its key is not marked obsolete, which is a
177 // different thing and must not be conflated.
178 assert_eq!(
179 AdapterKeyName::LabViewStdPrototype.replacement(),
180 Some(AdapterKeyName::LabView)
181 );
182 assert_eq!(
183 AdapterKeyName::CviStdPrototype.replacement(),
184 Some(AdapterKeyName::Cvi)
185 );
186 assert!(AdapterKeyName::LabViewStdPrototype.is_obsolete());
187 assert!(AdapterKeyName::CviStdPrototype.is_obsolete());
188
189 for current in [
190 AdapterKeyName::LabView,
191 AdapterKeyName::Cvi,
192 AdapterKeyName::LabViewNxg,
193 AdapterKeyName::DllFlex,
194 AdapterKeyName::NoneAdapter,
195 ] {
196 assert!(!current.is_obsolete(), "{current:?} is not obsolete");
197 assert_eq!(current.replacement(), None);
198 }
199 }
200
201 #[test]
202 fn the_maintained_labview_adapter_is_the_flexible_one() {
203 // Guards the mix-up this enum exists to prevent: reaching for "LabVIEW"
204 // must not land on the superseded standard-prototype key.
205 assert_eq!(AdapterKeyName::LabView.as_str(), "G Flexible VI Adapter");
206 assert_eq!(
207 AdapterKeyName::LabViewStdPrototype.as_str(),
208 "G Std Prototype Adapter"
209 );
210 assert_ne!(AdapterKeyName::LabViewNxg, AdapterKeyName::LabView);
211 }
212
213 #[test]
214 fn keys_are_distinct() {
215 // Two adapters sharing a key would make from_key ambiguous, and the
216 // type library does list one key under two names.
217 let keys = [
218 AdapterKeyName::NoneAdapter.as_str(),
219 AdapterKeyName::LabViewStdPrototype.as_str(),
220 AdapterKeyName::LabView.as_str(),
221 AdapterKeyName::LabViewNxg.as_str(),
222 AdapterKeyName::CviStdPrototype.as_str(),
223 AdapterKeyName::Cvi.as_str(),
224 AdapterKeyName::DllFlex.as_str(),
225 AdapterKeyName::Sequence.as_str(),
226 AdapterKeyName::Automation.as_str(),
227 AdapterKeyName::DotNet.as_str(),
228 AdapterKeyName::Python.as_str(),
229 AdapterKeyName::HtBasic.as_str(),
230 ];
231 let mut sorted = keys.to_vec();
232 sorted.sort_unstable();
233 sorted.dedup();
234 assert_eq!(sorted.len(), keys.len());
235 }
236}