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]);
    }
}