Skip to main content

quickfix/
dictionary.rs

1use std::{ffi::CString, fmt};
2
3use quickfix_ffi::{
4    FixDictionary_delete, FixDictionary_getBool, FixDictionary_getDay, FixDictionary_getDouble,
5    FixDictionary_getInt, FixDictionary_getStringLen, FixDictionary_hasKey, FixDictionary_new,
6    FixDictionary_readString, FixDictionary_setBool, FixDictionary_setDay, FixDictionary_setDouble,
7    FixDictionary_setInt, FixDictionary_setString, FixDictionary_t,
8};
9
10use crate::{
11    utils::{ffi_code_to_bool, ffi_code_to_result},
12    DayOfWeek, ForeignPropertyGetter, ForeignPropertySetter, QuickFixError,
13};
14
15/// For storage and retrieval of key/value pairs.
16pub struct Dictionary(pub(crate) FixDictionary_t);
17
18impl Dictionary {
19    /// Create a new empty struct.
20    pub fn new() -> Self {
21        Self::default()
22    }
23
24    /// Try to create new empty struct with a given name.
25    pub fn with_name(name: &str) -> Result<Self, QuickFixError> {
26        let c_name = CString::new(name)?;
27        unsafe { FixDictionary_new(c_name.as_ptr()) }
28            .map(Self)
29            .ok_or_else(QuickFixError::from_last_error)
30    }
31
32    /// Check if dictionary contains key.
33    pub fn contains(&self, key: &str) -> Result<bool, QuickFixError> {
34        let c_key = CString::new(key)?;
35        ffi_code_to_bool(unsafe { FixDictionary_hasKey(self.0, c_key.as_ptr()) })
36    }
37
38    /// Read value from dictionary for a given key.
39    pub fn get<T>(&self, key: &str) -> Result<T, QuickFixError>
40    where
41        Self: ForeignPropertyGetter<T>,
42    {
43        let c_key = CString::new(key)?;
44        self.ffi_get(c_key)
45    }
46
47    /// Write value into dictionary for a given key.
48    pub fn set<T>(&mut self, key: &str, value: T) -> Result<(), QuickFixError>
49    where
50        Self: ForeignPropertySetter<T>,
51    {
52        let c_key = CString::new(key)?;
53        self.ffi_set(c_key, value)
54    }
55}
56
57impl ForeignPropertyGetter<String> for Dictionary {
58    fn ffi_get(&self, key: CString) -> Result<String, QuickFixError> {
59        unsafe {
60            // Prepare output buffer
61            let buffer_len = FixDictionary_getStringLen(self.0, key.as_ptr())
62                .try_into()
63                .map_err(|_err| QuickFixError::from_last_error())?;
64
65            // Allocate buffer on rust side
66            let mut buffer = vec![0_u8; buffer_len as usize];
67            assert_eq!(buffer.len(), buffer_len as usize);
68
69            // Read text
70            ffi_code_to_result(FixDictionary_readString(
71                self.0,
72                key.as_ptr(),
73                buffer.as_mut_ptr().cast(),
74                buffer_len,
75            ))?;
76
77            // Convert to String
78            //
79            // NOTE: Here, I deliberately made the choice to drop C weird string / invalid UTF8 string
80            //       content. If this happen, there is not so much we can do about ...
81            //       Returning no error is sometime nicer, than an incomprehensible error.
82            let text = CString::from_vec_with_nul(buffer).unwrap_or_default();
83            Ok(text.to_string_lossy().to_string())
84        }
85    }
86}
87
88impl ForeignPropertySetter<String> for Dictionary {
89    fn ffi_set(&mut self, key: CString, value: String) -> Result<(), QuickFixError> {
90        self.ffi_set(key, value.as_str())
91    }
92}
93
94impl ForeignPropertySetter<&str> for Dictionary {
95    fn ffi_set(&mut self, key: CString, value: &str) -> Result<(), QuickFixError> {
96        let c_value = CString::new(value)?;
97        ffi_code_to_result(unsafe {
98            FixDictionary_setString(self.0, key.as_ptr(), c_value.as_ptr())
99        })
100    }
101}
102
103impl ForeignPropertyGetter<i32> for Dictionary {
104    fn ffi_get(&self, key: CString) -> Result<i32, QuickFixError> {
105        Ok(unsafe { FixDictionary_getInt(self.0, key.as_ptr()) })
106    }
107}
108
109impl ForeignPropertySetter<i32> for Dictionary {
110    fn ffi_set(&mut self, key: CString, value: i32) -> Result<(), QuickFixError> {
111        ffi_code_to_result(unsafe { FixDictionary_setInt(self.0, key.as_ptr(), value) })
112    }
113}
114
115impl ForeignPropertyGetter<f64> for Dictionary {
116    fn ffi_get(&self, key: CString) -> Result<f64, QuickFixError> {
117        Ok(unsafe { FixDictionary_getDouble(self.0, key.as_ptr()) })
118    }
119}
120
121impl ForeignPropertySetter<f64> for Dictionary {
122    fn ffi_set(&mut self, key: CString, value: f64) -> Result<(), QuickFixError> {
123        ffi_code_to_result(unsafe { FixDictionary_setDouble(self.0, key.as_ptr(), value) })
124    }
125}
126
127impl ForeignPropertyGetter<bool> for Dictionary {
128    fn ffi_get(&self, key: CString) -> Result<bool, QuickFixError> {
129        ffi_code_to_bool(unsafe { FixDictionary_getBool(self.0, key.as_ptr()) })
130    }
131}
132
133impl ForeignPropertySetter<bool> for Dictionary {
134    fn ffi_set(&mut self, key: CString, value: bool) -> Result<(), QuickFixError> {
135        ffi_code_to_result(unsafe { FixDictionary_setBool(self.0, key.as_ptr(), value as i8) })
136    }
137}
138
139impl ForeignPropertyGetter<DayOfWeek> for Dictionary {
140    fn ffi_get(&self, key: CString) -> Result<DayOfWeek, QuickFixError> {
141        DayOfWeek::try_from(unsafe { FixDictionary_getDay(self.0, key.as_ptr()) })
142    }
143}
144
145impl ForeignPropertySetter<DayOfWeek> for Dictionary {
146    fn ffi_set(&mut self, key: CString, value: DayOfWeek) -> Result<(), QuickFixError> {
147        ffi_code_to_result(unsafe { FixDictionary_setDay(self.0, key.as_ptr(), value as i32) })
148    }
149}
150
151impl fmt::Debug for Dictionary {
152    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
153        f.debug_tuple("Dictionary").finish()
154    }
155}
156
157impl Default for Dictionary {
158    fn default() -> Self {
159        Self::with_name("").expect("Fail to allocate Dictionary")
160    }
161}
162
163impl Drop for Dictionary {
164    fn drop(&mut self) {
165        unsafe { FixDictionary_delete(self.0) }
166    }
167}