Skip to main content

ironfix_fast/
operators.rs

1/******************************************************************************
2   Author: Joaquín Béjar García
3   Email: jb@taunais.com
4   Date: 27/1/26
5******************************************************************************/
6
7//! FAST field operators.
8//!
9//! Operators define how field values are encoded and decoded relative to
10//! previous values in the dictionary.
11
12use serde::{Deserialize, Serialize};
13
14/// FAST field operator types.
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
16pub enum Operator {
17    /// No operator - value is always present in stream.
18    #[default]
19    None,
20    /// Constant - value is never in stream, always uses initial value.
21    Constant,
22    /// Default - if absent, use initial value.
23    Default,
24    /// Copy - if absent, use previous value from dictionary.
25    Copy,
26    /// Increment - if absent, increment previous value by 1.
27    Increment,
28    /// Delta - value in stream is delta from previous value.
29    Delta,
30    /// Tail - value in stream replaces tail of previous value.
31    Tail,
32}
33
34impl Operator {
35    /// Returns true if this operator uses the dictionary.
36    #[must_use]
37    pub const fn uses_dictionary(&self) -> bool {
38        matches!(
39            self,
40            Self::Copy | Self::Increment | Self::Delta | Self::Tail
41        )
42    }
43
44    /// Returns true if this operator requires a presence map bit.
45    #[must_use]
46    pub const fn requires_pmap(&self) -> bool {
47        matches!(
48            self,
49            Self::None | Self::Copy | Self::Increment | Self::Delta | Self::Tail
50        )
51    }
52
53    /// Returns true if the value can be absent from the stream.
54    #[must_use]
55    pub const fn can_be_absent(&self) -> bool {
56        matches!(
57            self,
58            Self::Default | Self::Copy | Self::Increment | Self::Delta | Self::Tail
59        )
60    }
61}
62
63/// Dictionary scope for operator state.
64#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
65pub enum DictionaryScope {
66    /// Global dictionary shared across all templates.
67    #[default]
68    Global,
69    /// Template-specific dictionary.
70    Template,
71    /// Type-specific dictionary.
72    Type,
73}
74
75/// State for a dictionary entry.
76#[derive(Debug, Clone, Default)]
77pub enum DictionaryValue {
78    /// No value has been set.
79    #[default]
80    Undefined,
81    /// Value is explicitly empty/null.
82    Empty,
83    /// Integer value.
84    Int(i64),
85    /// Unsigned integer value.
86    UInt(u64),
87    /// String value.
88    String(String),
89    /// Byte sequence value.
90    Bytes(Vec<u8>),
91    /// Decimal value (mantissa, exponent).
92    Decimal(i64, i32),
93}
94
95impl DictionaryValue {
96    /// Returns true if the value is undefined.
97    #[must_use]
98    pub const fn is_undefined(&self) -> bool {
99        matches!(self, Self::Undefined)
100    }
101
102    /// Returns true if the value is empty.
103    #[must_use]
104    pub const fn is_empty(&self) -> bool {
105        matches!(self, Self::Empty)
106    }
107
108    /// Returns the value as an i64, if applicable.
109    #[must_use]
110    pub const fn as_i64(&self) -> Option<i64> {
111        match self {
112            Self::Int(v) => Some(*v),
113            _ => None,
114        }
115    }
116
117    /// Returns the value as a u64, if applicable.
118    #[must_use]
119    pub const fn as_u64(&self) -> Option<u64> {
120        match self {
121            Self::UInt(v) => Some(*v),
122            _ => None,
123        }
124    }
125
126    /// Returns the value as a string, if applicable.
127    #[must_use]
128    pub fn as_str(&self) -> Option<&str> {
129        match self {
130            Self::String(s) => Some(s),
131            _ => None,
132        }
133    }
134}
135
136#[cfg(test)]
137mod tests {
138    use super::*;
139
140    #[test]
141    fn test_operator_uses_dictionary() {
142        assert!(!Operator::None.uses_dictionary());
143        assert!(!Operator::Constant.uses_dictionary());
144        assert!(!Operator::Default.uses_dictionary());
145        assert!(Operator::Copy.uses_dictionary());
146        assert!(Operator::Increment.uses_dictionary());
147        assert!(Operator::Delta.uses_dictionary());
148        assert!(Operator::Tail.uses_dictionary());
149    }
150
151    #[test]
152    fn test_operator_requires_pmap() {
153        assert!(Operator::None.requires_pmap());
154        assert!(!Operator::Constant.requires_pmap());
155        assert!(!Operator::Default.requires_pmap());
156        assert!(Operator::Copy.requires_pmap());
157    }
158
159    #[test]
160    fn test_dictionary_value() {
161        let undefined = DictionaryValue::Undefined;
162        assert!(undefined.is_undefined());
163
164        let int_val = DictionaryValue::Int(42);
165        assert_eq!(int_val.as_i64(), Some(42));
166
167        let str_val = DictionaryValue::String("test".to_string());
168        assert_eq!(str_val.as_str(), Some("test"));
169    }
170}