1use std::{ffi::CString, fmt, mem::ManuallyDrop};
2
3use quickfix_ffi::{
4 FixMessage_addGroup, FixMessage_copy, FixMessage_copyGroup, FixMessage_copyHeader,
5 FixMessage_copyTrailer, FixMessage_delete, FixMessage_fromString, FixMessage_getField,
6 FixMessage_getGroupRef, FixMessage_getHeaderRef, FixMessage_getStringLen,
7 FixMessage_getTrailerRef, FixMessage_new, FixMessage_readString, FixMessage_removeField,
8 FixMessage_setField, FixMessage_t,
9};
10
11use crate::{
12 group::Group,
13 header::Header,
14 trailer::Trailer,
15 utils::{ffi_code_to_result, read_checked_cstr},
16 FieldMap, IntoFixValue, QuickFixError,
17};
18
19pub struct Message(pub(crate) FixMessage_t);
21
22impl Message {
23 pub fn new() -> Self {
25 Self::default()
26 }
27
28 pub fn try_from_text(text: &str) -> Result<Self, QuickFixError> {
30 let ffi_text = CString::new(text)?;
31 unsafe { FixMessage_fromString(ffi_text.as_ptr()) }
32 .map(Self)
33 .ok_or_else(QuickFixError::from_last_error)
34 }
35
36 pub fn to_fix_raw_bytes(&self) -> Result<Vec<u8>, QuickFixError> {
46 unsafe {
47 let buffer_len = FixMessage_getStringLen(self.0)
49 .try_into()
50 .map_err(|_err| QuickFixError::from_last_error())?;
51
52 let mut buffer = vec![0_u8; buffer_len as usize];
54 assert_eq!(buffer.len(), buffer_len as usize);
55
56 ffi_code_to_result(FixMessage_readString(
58 self.0,
59 buffer.as_mut_ptr().cast(),
60 buffer_len,
61 ))?;
62
63 Ok(buffer)
64 }
65 }
66
67 pub fn to_fix_string(&self) -> Result<String, QuickFixError> {
77 let buffer = self.to_fix_raw_bytes()?;
78
79 let text = CString::from_vec_with_nul(buffer).unwrap_or_default();
85 Ok(text.to_string_lossy().into())
86 }
87
88 pub fn clone_header(&self) -> Header {
94 unsafe { FixMessage_copyHeader(self.0) }
95 .map(Header)
96 .expect("Fail to allocate new Header")
97 }
98
99 pub fn with_header<T, F>(&self, f: F) -> T
106 where
107 F: FnOnce(&Header) -> T,
108 {
109 let ptr =
110 unsafe { FixMessage_getHeaderRef(self.0) }.expect("Fail to get ptr on message header");
111
112 let obj = ManuallyDrop::new(Header(ptr));
113 f(&obj)
114 }
115
116 pub fn with_header_mut<T, F>(&mut self, f: F) -> T
123 where
124 F: FnOnce(&mut Header) -> T,
125 {
126 let ptr =
127 unsafe { FixMessage_getHeaderRef(self.0) }.expect("Fail to get ptr on message header");
128
129 let mut obj = ManuallyDrop::new(Header(ptr));
130 f(&mut obj)
131 }
132
133 pub fn clone_trailer(&self) -> Trailer {
139 unsafe { FixMessage_copyTrailer(self.0) }
140 .map(Trailer)
141 .expect("Fail to allocate new Trailer")
142 }
143
144 pub fn with_trailer<T, F>(&self, f: F) -> T
151 where
152 F: FnOnce(&Trailer) -> T,
153 {
154 let ptr = unsafe { FixMessage_getTrailerRef(self.0) }
155 .expect("Fail to get ptr on message trailer");
156
157 let obj = ManuallyDrop::new(Trailer(ptr));
158 f(&obj)
159 }
160
161 pub fn with_trailer_mut<T, F>(&mut self, f: F) -> T
168 where
169 F: FnOnce(&mut Trailer) -> T,
170 {
171 let ptr = unsafe { FixMessage_getTrailerRef(self.0) }
172 .expect("Fail to get ptr on message trailer");
173
174 let mut obj = ManuallyDrop::new(Trailer(ptr));
175 f(&mut obj)
176 }
177
178 pub fn with_group<T, F>(&self, index: i32, tag: i32, f: F) -> Option<T>
180 where
181 F: FnOnce(&Group) -> T,
182 {
183 if let Some(ptr) = unsafe { FixMessage_getGroupRef(self.0, index, tag) } {
184 let obj = ManuallyDrop::new(Group(ptr));
185 Some(f(&obj))
186 } else {
187 None
188 }
189 }
190
191 pub fn with_group_mut<T, F>(&mut self, index: i32, tag: i32, f: F) -> Option<T>
193 where
194 F: FnOnce(&mut Group) -> T,
195 {
196 if let Some(ptr) = unsafe { FixMessage_getGroupRef(self.0, index, tag) } {
197 let mut obj = ManuallyDrop::new(Group(ptr));
198 Some(f(&mut obj))
199 } else {
200 None
201 }
202 }
203}
204
205impl FieldMap for Message {
206 fn get_field(&self, tag: i32) -> Option<String> {
207 unsafe { FixMessage_getField(self.0, tag) }.map(read_checked_cstr)
208 }
209
210 fn set_field<V: IntoFixValue>(&mut self, tag: i32, value: V) -> Result<(), QuickFixError> {
211 let fix_value = value.into_fix_value()?;
212 ffi_code_to_result(unsafe { FixMessage_setField(self.0, tag, fix_value.as_ptr()) })
213 }
214
215 fn remove_field(&mut self, tag: i32) -> Result<(), QuickFixError> {
216 ffi_code_to_result(unsafe { FixMessage_removeField(self.0, tag) })
217 }
218
219 fn add_group(&mut self, group: &Group) -> Result<(), QuickFixError> {
220 ffi_code_to_result(unsafe { FixMessage_addGroup(self.0, group.0) })?;
221 Ok(())
222 }
223
224 fn clone_group(&self, index: i32, tag: i32) -> Option<Group> {
225 unsafe { FixMessage_copyGroup(self.0, index, tag) }.map(Group)
226 }
227}
228
229impl Clone for Message {
230 fn clone(&self) -> Self {
231 Self(unsafe { FixMessage_copy(self.0) }.expect("Fail to clone Message"))
232 }
233}
234
235impl fmt::Debug for Message {
236 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
237 let mut printer = f.debug_tuple("Message");
238
239 if let Ok(txt) = self.to_fix_string() {
240 printer.field(&txt.replace(1 as char, "|"));
241 }
242
243 printer.finish()
244 }
245}
246
247impl Default for Message {
248 fn default() -> Self {
249 unsafe { FixMessage_new() }
250 .map(Self)
251 .expect("Fail to allocate new Message")
252 }
253}
254
255impl Drop for Message {
256 fn drop(&mut self) {
257 unsafe { FixMessage_delete(self.0) }
258 }
259}