Skip to main content

rs_teststand/
enums.rs

1//! Type-safe Rust enums for TestStand™ options, types, and flags.
2//!
3//! All discriminants are sourced from TestStand™ type library ground truth.
4
5/// Property value types (`PropValType_*`).
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
7#[repr(i32)]
8pub enum PropValType {
9    /// Container object (`PropValType_Container = 0`).
10    Container = 0,
11    /// String property (`PropValType_String = 1`).
12    String = 1,
13    /// Boolean property (`PropValType_Boolean = 2`).
14    Boolean = 2,
15    /// Numeric property (`PropValType_Number = 3`).
16    Number = 3,
17    /// Named type instance (`PropValType_NamedType = 4`).
18    NamedType = 4,
19    /// Property reference (`PropValType_Reference = 5`).
20    Reference = 5,
21    /// Array property (`PropValType_Array = 6`).
22    Array = 6,
23    /// Enum property (`PropValType_Enum = 7`).
24    Enum = 7,
25}
26
27bitflags::bitflags! {
28    /// A set of property value type flags (`PropValTypeFlag_*`).
29    ///
30    /// A bitmask, not an enumeration: the engine combines flags, and a caller
31    /// must be able to express "number or string".
32    ///
33    /// ```
34    /// use rs_teststand::PropertyValueTypeFlags as Flags;
35    ///
36    /// let numeric_or_text = Flags::NUMBER | Flags::STRING;
37    /// assert!(numeric_or_text.contains(Flags::STRING));
38    /// assert!(!numeric_or_text.contains(Flags::CONTAINER));
39    /// ```
40    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
41    pub struct PropertyValueTypeFlags: i32 {
42        /// No flags set.
43        const NONE = 0;
44        /// Matches any property type (`PropValTypeFlag_Any`).
45        const ANY = -1;
46        /// Boolean property (`PropValTypeFlag_Boolean`).
47        const BOOLEAN = 1;
48        /// Numeric property (`PropValTypeFlag_Number`).
49        const NUMBER = 2;
50        /// String property (`PropValTypeFlag_String`).
51        const STRING = 4;
52        /// Reference property (`PropValTypeFlag_Reference`).
53        const REFERENCE = 8;
54        /// Container property (`PropValTypeFlag_Container`).
55        const CONTAINER = 16;
56        /// Named type instance (`PropValTypeFlag_NamedType`).
57        const NAMED_TYPE = 32;
58        /// Boolean array (`PropValTypeFlag_BooleanArray`).
59        const BOOLEAN_ARRAY = 64;
60        /// Numeric array (`PropValTypeFlag_NumberArray`).
61        const NUMBER_ARRAY = 128;
62        /// String array (`PropValTypeFlag_StringArray`).
63        const STRING_ARRAY = 256;
64        /// Reference array (`PropValTypeFlag_ReferenceArray`).
65        const REFERENCE_ARRAY = 512;
66        /// Container array (`PropValTypeFlag_ContainerArray`).
67        const CONTAINER_ARRAY = 1024;
68        /// Array of a named type (`PropValTypeFlag_ArrayOfNamedType`).
69        const ARRAY_OF_NAMED_TYPE = 2048;
70        /// Empty / absent type (`PropValTypeFlag_Nothing`).
71        const NOTHING = 4096;
72        /// Object reference (`PropValTypeFlag_Object`).
73        const OBJECT = 16384;
74        /// Plain reference (`PropValTypeFlag_PlainReference`).
75        const PLAIN_REFERENCE = 32768;
76        /// Plain container (`PropValTypeFlag_PlainContainer`).
77        const PLAIN_CONTAINER = 65536;
78        /// Enum type (`PropValTypeFlag_Enum`).
79        const ENUM = 131_072;
80    }
81}
82
83/// Step execution groups in a sequence (`StepGroup_*`).
84///
85/// A sequence holds three ordered lists rather than one. Setup runs first, Main
86/// carries the test, and Cleanup runs last, including after Main fails, which
87/// is what makes it the place for anything that must happen regardless.
88#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
89#[repr(i32)]
90pub enum StepGroup {
91    /// Setup step group (`StepGroup_Setup = 0`).
92    Setup = 0,
93    /// Main step group (`StepGroup_Main = 1`).
94    Main = 1,
95    /// Cleanup step group (`StepGroup_Cleanup = 2`).
96    Cleanup = 2,
97}
98
99impl StepGroup {
100    /// Every group, in execution order.
101    pub const ALL: [Self; 3] = [Self::Setup, Self::Main, Self::Cleanup];
102
103    /// The value the COM boundary expects.
104    #[must_use]
105    pub const fn bits(self) -> i32 {
106        self as i32
107    }
108
109    /// Reads a raw value, returning it unchanged when unrecognized.
110    ///
111    /// # Errors
112    /// The raw value, when it matches no known group.
113    pub const fn from_bits(raw: i32) -> Result<Self, i32> {
114        Ok(match raw {
115            0 => Self::Setup,
116            1 => Self::Main,
117            2 => Self::Cleanup,
118            other => return Err(other),
119        })
120    }
121}
122
123/// Execution running states (`ExecRunState_*`).
124#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
125#[repr(i32)]
126pub enum ExecRunState {
127    /// Execution is currently running (`ExecRunState_Running = 1`).
128    Running = 1,
129    /// Execution is paused (`ExecRunState_Paused = 2`).
130    Paused = 2,
131    /// Execution is stopped (`ExecRunState_Stopped = 3`).
132    Stopped = 3,
133}
134
135/// Execution termination states (`ExecTermState_*`).
136#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
137#[repr(i32)]
138pub enum ExecTermState {
139    /// Normal execution state (`ExecTermState_Normal = 1`).
140    Normal = 1,
141    /// Execution is terminating (`ExecTermState_Terminating = 2`).
142    Terminating = 2,
143    /// Interactive termination (`ExecTermState_TerminatingInteractive = 3`).
144    TerminatingInteractive = 3,
145    /// Execution is aborting (`ExecTermState_Aborting = 4`).
146    Aborting = 4,
147    /// Killing execution threads (`ExecTermState_KillingThreads = 5`).
148    KillingThreads = 5,
149}
150
151/// Search directory categories (`SearchDirectoryType_*`).
152// Every variant ends in `Dir`, which trips `clippy::enum_variant_names`. The
153// names are deliberate: they mirror the type library one-for-one
154// (`SearchDirectoryType_TestStandDir` -> `TestStandDir`) per the twin-API rule,
155// so renaming them to satisfy the lint would make the mapping unpredictable.
156#[allow(
157    clippy::enum_variant_names,
158    reason = "variant names mirror the type library"
159)]
160#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
161#[repr(i32)]
162pub enum SearchDirectoryType {
163    /// TestStand root directory (`SearchDirectoryType_TestStandDir = 1`).
164    TestStandDir = 1,
165    /// TestStand bin directory (`SearchDirectoryType_TestStandBinDir = 2`).
166    TestStandBinDir = 2,
167    /// Adapter support directory (`SearchDirectoryType_AdapterSupportDir = 3`).
168    AdapterSupportDir = 3,
169    /// Application directory (`SearchDirectoryType_ApplicationDir = 4`).
170    ApplicationDir = 4,
171    /// Initial working directory (`SearchDirectoryType_InitialWorkingDir = 5`).
172    InitialWorkingDir = 5,
173    /// Windows system directory (`SearchDirectoryType_WindowsSystemDir = 6`).
174    WindowsSystemDir = 6,
175    /// Windows directory (`SearchDirectoryType_WindowsDir = 7`).
176    WindowsDir = 7,
177    /// PATH environment variable directory (`SearchDirectoryType_PathEnvironmentVarDir = 8`).
178    PathEnvironmentVarDir = 8,
179    /// Current sequence file directory (`SearchDirectoryType_CurrentSequenceFileDir = 9`).
180    CurrentSequenceFileDir = 9,
181    /// User/Public components directory (`SearchDirectoryType_UserComponentsDir = 11`).
182    UserComponentsDir = 11,
183    /// NI components directory (`SearchDirectoryType_NIComponentsDir = 12`).
184    NIComponentsDir = 12,
185    /// Current workspace directory (`SearchDirectoryType_CurrentWorkspaceDir = 13`).
186    CurrentWorkspaceDir = 13,
187    /// Containing project directory (`SearchDirectoryType_ContainingProjectDir = 14`).
188    ContainingProjectDir = 14,
189    /// Explicit user directory (`SearchDirectoryType_ExplicitDir = 15`).
190    ExplicitDir = 15,
191    /// TestStand public directory (`SearchDirectoryType_TestStandPublicDir = 16`).
192    TestStandPublicDir = 16,
193}
194
195/// Options for opening workspace files (`OpenWorkspaceFileOptions_*`).
196#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
197#[repr(i32)]
198pub enum OpenWorkspaceFileOptions {
199    /// No options (`OpenWorkspaceFile_NoOptions = 0`).
200    NoOptions = 0,
201    /// Ignore missing files (`OpenWorkspaceFile_IgnoreMissingFiles = 1`).
202    IgnoreMissingFiles = 1,
203    /// Search current directory (`OpenWorkspaceFile_SearchCurrentDirectory = 2`).
204    SearchCurrentDirectory = 2,
205    /// Use search directories (`OpenWorkspaceFile_UseSearchDirectories = 4`).
206    UseSearchDirectories = 4,
207}
208
209/// Options for saving workspace files (`SaveWorkspaceFileOptions_*`).
210#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
211#[repr(i32)]
212pub enum SaveWorkspaceFileOptions {
213    /// No options (`SaveWorkspaceFile_NoOptions = 0`).
214    NoOptions = 0,
215    /// Prompt user (`SaveWorkspaceFile_PromptUser = 1`).
216    PromptUser = 1,
217    /// Skip workspace file (`SaveWorkspaceFile_SkipWorkspaceFile = 2`).
218    SkipWorkspaceFile = 2,
219    /// Skip read-only files (`SaveWorkspaceFile_SkipReadOnlyFiles = 4`).
220    SkipReadOnlyFiles = 4,
221}
222
223impl TryFrom<i32> for SearchDirectoryType {
224    type Error = i32;
225
226    fn try_from(value: i32) -> Result<Self, Self::Error> {
227        match value {
228            1 => Ok(Self::TestStandDir),
229            2 => Ok(Self::TestStandBinDir),
230            3 => Ok(Self::AdapterSupportDir),
231            4 => Ok(Self::ApplicationDir),
232            5 => Ok(Self::InitialWorkingDir),
233            6 => Ok(Self::WindowsSystemDir),
234            7 => Ok(Self::WindowsDir),
235            8 => Ok(Self::PathEnvironmentVarDir),
236            9 => Ok(Self::CurrentSequenceFileDir),
237            11 => Ok(Self::UserComponentsDir),
238            12 => Ok(Self::NIComponentsDir),
239            13 => Ok(Self::CurrentWorkspaceDir),
240            14 => Ok(Self::ContainingProjectDir),
241            15 => Ok(Self::ExplicitDir),
242            16 => Ok(Self::TestStandPublicDir),
243            other => Err(other),
244        }
245    }
246}
247
248impl std::fmt::Display for SearchDirectoryType {
249    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
250        write!(f, "{self:?}")
251    }
252}
253
254impl std::fmt::Display for PropValType {
255    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
256        write!(f, "{self:?}")
257    }
258}
259
260#[cfg(test)]
261mod tests {
262    use super::*;
263
264    #[test]
265    fn discriminants_match_tlb_ground_truth() {
266        assert_eq!(PropValType::Container as i32, 0);
267        assert_eq!(PropValType::String as i32, 1);
268        assert_eq!(PropValType::Boolean as i32, 2);
269        assert_eq!(PropValType::Number as i32, 3);
270        assert_eq!(PropValType::Enum as i32, 7);
271
272        assert_eq!(PropertyValueTypeFlags::ANY.bits(), -1);
273        assert_eq!(PropertyValueTypeFlags::CONTAINER.bits(), 16);
274        assert_eq!(PropertyValueTypeFlags::ENUM.bits(), 131_072);
275    }
276
277    #[test]
278    fn value_type_flags_combine_and_round_trip() {
279        // The engine treats these as a mask: a combination is a legal value and
280        // must survive a round trip through the COM boundary unchanged.
281        let mask = PropertyValueTypeFlags::NUMBER | PropertyValueTypeFlags::STRING;
282        assert_eq!(mask.bits(), 6);
283        assert!(mask.contains(PropertyValueTypeFlags::NUMBER));
284        assert!(mask.contains(PropertyValueTypeFlags::STRING));
285        assert!(!mask.contains(PropertyValueTypeFlags::CONTAINER));
286        assert_eq!(PropertyValueTypeFlags::from_bits_retain(mask.bits()), mask);
287
288        let mut acc = PropertyValueTypeFlags::NONE;
289        assert!(acc.is_empty());
290        acc |= PropertyValueTypeFlags::CONTAINER;
291        assert_eq!(acc, PropertyValueTypeFlags::CONTAINER);
292        assert_eq!(
293            acc & PropertyValueTypeFlags::CONTAINER,
294            PropertyValueTypeFlags::CONTAINER
295        );
296    }
297
298    #[test]
299    fn remaining_discriminants_match_tlb_ground_truth() {
300        assert_eq!(StepGroup::Setup as i32, 0);
301        assert_eq!(StepGroup::Main as i32, 1);
302        assert_eq!(StepGroup::Cleanup as i32, 2);
303
304        assert_eq!(ExecRunState::Running as i32, 1);
305        assert_eq!(ExecTermState::Normal as i32, 1);
306
307        assert_eq!(SearchDirectoryType::ExplicitDir as i32, 15);
308        assert_eq!(
309            SearchDirectoryType::try_from(15),
310            Ok(SearchDirectoryType::ExplicitDir)
311        );
312
313        assert_eq!(OpenWorkspaceFileOptions::NoOptions as i32, 0);
314        assert_eq!(OpenWorkspaceFileOptions::UseSearchDirectories as i32, 4);
315
316        assert_eq!(SaveWorkspaceFileOptions::NoOptions as i32, 0);
317        assert_eq!(SaveWorkspaceFileOptions::SkipReadOnlyFiles as i32, 4);
318    }
319}