Skip to main content

labview_interop/types/
lv_status.rs

1//! Module for handling of LabVIEW status codes.
2//!
3//! Although not a unique type in LabVIEW, the status code holds special semantic meaning
4//! which is why it is given its own type.
5
6use crate::errors::{LVInteropError, MgError};
7use std::error::Error;
8use std::fmt::Display;
9
10/// ´LVStatusCode´ is a transparent newtype on i32 to represent all potential error codes and SUCCESS (0) as a success value.
11///
12/// This kind of status code corresponds to the Rust Result types.
13/// Therefore, it is named status and not error on purpose. There is no checks or guarantees if the code is a valid range or has an official labview
14/// definition.
15///
16/// # Examples
17///
18/// ```
19/// use labview_interop::types::LVStatusCode;
20/// let status = LVStatusCode::from(42);
21///
22/// assert_eq!(status, 42.into());
23/// ```
24#[repr(transparent)]
25#[derive(Debug, Clone, Copy, Eq, PartialEq)]
26pub struct LVStatusCode(i32);
27
28impl Display for LVStatusCode {
29    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
30        #[cfg(not(feature = "link"))]
31        write!(f, "LVStatusCode: {}", self.0)?;
32        #[cfg(feature = "link")]
33        write!(f, "LVStatusCode: {} - {}", self.0, self.description())?;
34        Ok(())
35    }
36}
37
38impl Error for LVStatusCode {}
39
40impl LVStatusCode {
41    pub const SUCCESS: LVStatusCode = LVStatusCode(0);
42
43    ///this will convert the LVStatusCode to either Ok(T) or Err(LVInteropError(LabviewMgError)) or Err(LVInteropError)
44    ///mostly for our internal use
45    // Unused if link disabled. Allow dead code to avoid warnings.
46    #[allow(dead_code)]
47    pub(crate) fn to_specific_result<T>(self, success_value: T) -> crate::errors::Result<T> {
48        if self == Self::SUCCESS {
49            Ok(success_value)
50        } else {
51            match MgError::try_from(self) {
52                Ok(mg_err) => Err(mg_err.into()),
53                Err(inter_err) => Err(inter_err),
54            }
55        }
56    }
57
58    /// this will convert the LVStatusCode to the generic LVError with no checks of validity
59    pub fn to_generic_result<T>(self, success_value: T) -> Result<T, LVInteropError> {
60        if self == Self::SUCCESS {
61            Ok(success_value)
62        } else {
63            Err(LVInteropError::LabviewError(self))
64        }
65    }
66}
67
68// From<i32> vice versa implemented, but not Deref (do not want to inherit other math operations)
69impl From<i32> for LVStatusCode {
70    fn from(value: i32) -> LVStatusCode {
71        LVStatusCode(value)
72    }
73}
74
75impl From<LVStatusCode> for i32 {
76    fn from(code: LVStatusCode) -> i32 {
77        code.0
78    }
79}
80
81#[cfg(feature = "link")]
82mod lv_status_link_features {
83    use super::*;
84    use std::borrow::Cow;
85    impl LVStatusCode {
86        pub fn description(&self) -> Cow<'static, str> {
87            use crate::labview::memory_api;
88            use crate::types::LStrHandle;
89            use std::mem::MaybeUninit;
90
91            static DEFAULT_STRING: &str = "LabVIEW-Interop: Description not retrievable";
92            let mut error_text_ptr = MaybeUninit::<LStrHandle>::uninit();
93
94            let memory_api = match memory_api() {
95                Ok(api) => api,
96                Err(_) => return Cow::Borrowed(DEFAULT_STRING),
97            };
98
99            unsafe {
100                if memory_api
101                    .error_code_description(self.0, error_text_ptr.as_mut_ptr() as *mut usize)
102                {
103                    let error_text_ptr = error_text_ptr.assume_init();
104                    let desc = error_text_ptr.to_rust_string().to_string();
105                    return Cow::Owned(desc);
106                }
107            }
108            Cow::Borrowed(DEFAULT_STRING)
109        }
110    }
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116
117    #[test]
118    fn test_error_lvstatuscode_from_i32() {
119        let status = LVStatusCode::from(0);
120        assert_eq!(status, LVStatusCode::SUCCESS);
121
122        let status = LVStatusCode::from(1);
123        assert_eq!(status, LVStatusCode(1));
124
125        let status: LVStatusCode = 42.into();
126        assert_eq!(status, LVStatusCode(42));
127    }
128
129    // test transparency of status type
130    #[test]
131    fn test_error_lvstatuscode_from_externc() {
132        // Mock the external C function
133        unsafe extern "C" fn mock_externc() -> i32 {
134            542_002 // Simulate a C function returning an `i32`
135        }
136
137        fn post_lv_user_event_safe() -> LVStatusCode {
138            let result: i32 = unsafe { mock_externc() };
139
140            // Transmute the i32 result to LVStatusCode
141            unsafe { std::mem::transmute(result) }
142        }
143
144        let lv_status = post_lv_user_event_safe();
145
146        assert_eq!(lv_status, LVStatusCode(542_002));
147    }
148}