Skip to main content

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}