1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
//! A wide collection of [`FieldType`](crate::FieldType) implementors.
//!
//! | FIX datatype | Suggested [`FieldType`] implementors |
//! |----------------------------|------------------------------------------------------------------------------------|
//! | `int` | [`u32`], [`i32`], [`u64`], [`i64`], [`u128`], [`i128`]. |
//! | `Length` | [`usize`]. |
//! | `NumInGroup` | [`usize`]. |
//! | `SeqNum` | [`u64`]. |
//! | `TagNum` | [`TagU32`](crate::TagU32). |
//! | `DayOfMonth` | [`u32`]. |
//! | `float`, `Price`, etc. | [`f32`], [`f64`], [`struct@rust_decimal::Decimal`], [`struct@decimal::d128`]. |
//! | `Boolean` | [`bool`]. |
//! | `char` | [`u8`] [^1]. |
//! | `String` | [`Vec<u8>`], `&[u8]`.[^1] |
//! | `data` | [`Vec<u8>`], `&[u8]` (also [`String`], [`str`] for UTF-8 content). |
//! | `MultipleCharValue` | [`MultipleChars`] [^1]. |
//! | `MultipleValueString` | [`MultipleStrings`] [^1]. |
//! | `Country` | [`Country`]. |
//! | `Currency` | [`Currency`]. |
//! | `Exchange` | [`Exchange`]. |
//! | `month-year` | [`MonthYear`]. |
//! | `UTCTimeOnly` | [`Time`], [`chrono::NaiveTime`]. |
//! | `UTCDateOnly` | [`Date`], [`chrono::NaiveDate`]. |
//! | `UTCTimestamp` | [`Timestamp`], [`chrono::NaiveDateTime`]. |
//! | `TZTimestamp` | [`TzTimestamp`], [`chrono::DateTime<chrono::FixedOffset>`]. |
//! | `LocalMktDate` | [`Date`], [`chrono::NaiveDate`]. |
//! | `TZTimeOnly` | [`TzTime`], |
//!
//! The above table provides some useful guidelines that work for the vast
//! majority of use cases.
//!
//! # Quick tour of [`FieldType`]
//!
//! ```
//! use hotfix_encoding::field_access::FieldType;
//! use hotfix_encoding::field_types::Timestamp;
//!
//! let bytes = b"20130422-12:30:00.000";
//!
//! // You can use `FieldType::deserialize` to parse data fields.
//! let timestamp = Timestamp::deserialize(bytes).unwrap();
//! assert_eq!(timestamp.date().year(), 2013);
//!
//! // `FieldType::deserialize_lossy` is like `FieldType::deserialize`, but it's
//! // allowed to skip some format verification for the sake of speed.
//! assert!(u32::deserialize(b"invalid integer").is_err());
//! assert!(u32::deserialize_lossy(b"invalid integer").is_ok());
//!
//! let mut buffer: Vec<u8> = vec![];
//! // Use `FieldType::serialize` to write values to buffers.
//! 1337u32.serialize(&mut buffer);
//! assert_eq!(&buffer[..], b"1337" as &[u8]);
//! ```
//!
//! [^1]: With the exception of datatype `data`, FIX mandates a single-byte
//! encoding (Latin alphabet No. 1 by default), while Rust strings are UTF-8,
//! which is a multibyte encoding. These are *not* compatible. Watch out!
mod checksum;
mod date;
mod monthyear;
mod multiple_chars;
mod multiple_strings;
mod primitives;
mod tagu32;
mod time;
mod timestamp;
mod tz;
mod tz_time;
mod tz_timestamp;
#[cfg(feature = "utils-chrono")]
mod utils_chrono;
#[cfg(feature = "utils-decimal")]
mod utils_decimal;
#[cfg(feature = "utils-rust-decimal")]
mod utils_rust_decimal;
pub use checksum::CheckSum;
pub use date::Date;
pub use monthyear::MonthYear;
pub use multiple_chars::MultipleChars;
pub use multiple_strings::MultipleStrings;
pub use time::Time;
pub use timestamp::Timestamp;
pub use tz::Tz;
pub use tz_time::TzTime;
pub use tz_timestamp::TzTimestamp;
use crate::field_access::FieldType;
/// Type alias for ISO 3166-1 alpha-2 strings (two-letter country codes).
pub type Country = [u8; 2];
/// Type alias for ISO 4217 alpha codes (three-letter currency codes).
pub type Currency = [u8; 3];
/// Type alias for four-letter *Market Identifier Codes* (MICs) as defined by
/// ISO 10383.
pub type Exchange = [u8; 4];
pub(crate) const ERR_UTF8: &str = "Invalid byte sequence; expected UTF-8 valid bytes.";
pub(crate) const ERR_INT_INVALID: &str = "Invalid integer digits.";
pub(crate) const ERR_TIME: &str = "Invalid time.";
/// Zero-padding for integers; see [`FieldType::SerializeSettings`].
#[derive(Debug, Copy, Clone, Default)]
pub struct ZeroPadding(pub usize);
/// Tries to [`FieldType::serialize`] an `item`, then to
/// [`FieldType::deserialize`] it, and finally checks for equality with the
/// initial data. [`FieldType::deserialize_lossy`] is then
/// tested in the same manner.
pub fn test_utility_verify_serialization_behavior<T>(item: T) -> bool
where
T: for<'a> FieldType<'a> + PartialEq,
{
let serialized = item.to_bytes();
let bytes = &serialized[..];
let deserialized = T::deserialize(bytes).ok().unwrap();
let deserialized_lossy = T::deserialize_lossy(bytes).ok().unwrap();
deserialized == item && deserialized_lossy == item
}
#[cfg(test)]
mod test {
use super::FieldType;
use quickcheck_macros::quickcheck;
#[test]
fn serialize_bools() {
let mut buffer = Vec::new();
assert_eq!(true.serialize(&mut buffer), 1);
assert_eq!(false.serialize(&mut buffer), 1);
assert_eq!(&buffer[..], b"YN" as &[u8]);
}
#[quickcheck]
fn serialize_bytes(data: Vec<Vec<u8>>) -> bool {
let mut buffer = Vec::new();
for slice in data.iter() {
assert_eq!((&slice[..]).serialize(&mut buffer), slice.len());
}
buffer[..] == data.iter().flatten().copied().collect::<Vec<u8>>()[..]
}
#[quickcheck]
fn u32_serialize(n: u32) -> bool {
let mut buffer = Vec::new();
let s = FieldType::to_string(&n);
let bytes = s.as_bytes();
let len = n.serialize(&mut buffer);
bytes == buffer.as_slice() && len == bytes.len()
}
#[test]
fn serialize_country() {
let mut buffer = Vec::new();
assert_eq!(b"IT".serialize(&mut buffer), 2);
assert_eq!(&buffer[..], b"IT" as &[u8]);
}
#[test]
fn serialize_currency() {
let mut buffer = Vec::new();
assert_eq!(b"USD".serialize(&mut buffer), 3);
assert_eq!(&buffer[..], b"USD" as &[u8]);
}
}