1use crate::error::ConversionError;
5use crate::message::Tag;
6use crate::value::{FixDecode, FixEncode};
7
8pub trait Field {
12 const TAG: Tag;
13 type Value: FixEncode + FixDecode;
14}
15
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct TagValue {
18 pub tag: Tag,
19 pub value: Vec<u8>,
20}
21
22#[derive(Debug, Clone, Default, PartialEq, Eq)]
28pub struct FieldMap {
29 fields: Vec<TagValue>,
30}
31
32impl FieldMap {
33 pub fn new() -> Self {
34 Self::default()
35 }
36
37 pub fn len(&self) -> usize {
38 self.fields.len()
39 }
40
41 pub fn is_empty(&self) -> bool {
42 self.fields.is_empty()
43 }
44
45 pub fn clear(&mut self) {
46 self.fields.clear();
47 }
48
49 pub fn contains(&self, tag: Tag) -> bool {
50 self.fields.iter().any(|f| f.tag == tag)
51 }
52
53 pub fn get_raw(&self, tag: Tag) -> Option<&[u8]> {
55 self.fields.iter().find(|f| f.tag == tag).map(|f| f.value.as_slice())
56 }
57
58 pub fn get<T: FixDecode>(&self, tag: Tag) -> Result<T, ConversionError> {
60 let raw = self.get_raw(tag).ok_or(ConversionError::FieldNotFound { tag })?;
61 T::decode(tag, raw)
62 }
63
64 pub fn get_opt<T: FixDecode>(&self, tag: Tag) -> Result<Option<T>, ConversionError> {
66 match self.get_raw(tag) {
67 Some(raw) => T::decode(tag, raw).map(Some),
68 None => Ok(None),
69 }
70 }
71
72 pub fn get_string(&self, tag: Tag) -> Result<String, ConversionError> {
73 self.get::<String>(tag)
74 }
75
76 pub fn get_field<F: Field>(&self) -> Result<F::Value, ConversionError> {
79 self.get::<F::Value>(F::TAG)
80 }
81
82 pub fn get_field_opt<F: Field>(&self) -> Result<Option<F::Value>, ConversionError> {
83 self.get_opt::<F::Value>(F::TAG)
84 }
85
86 pub fn set_field<F: Field>(&mut self, value: F::Value) {
87 self.set(F::TAG, value);
88 }
89
90 pub fn has_field<F: Field>(&self) -> bool {
91 self.contains(F::TAG)
92 }
93
94 pub fn set(&mut self, tag: Tag, value: impl FixEncode) {
96 let mut buf = Vec::new();
97 value.encode(&mut buf);
98 self.set_raw(tag, buf);
99 }
100
101 pub fn set_raw(&mut self, tag: Tag, value: Vec<u8>) {
102 match self.fields.iter_mut().find(|f| f.tag == tag) {
103 Some(f) => f.value = value,
104 None => self.fields.push(TagValue { tag, value }),
105 }
106 }
107
108 pub fn push(&mut self, tag: Tag, value: impl FixEncode) {
110 let mut buf = Vec::new();
111 value.encode(&mut buf);
112 self.fields.push(TagValue { tag, value: buf });
113 }
114
115 pub fn remove(&mut self, tag: Tag) -> bool {
117 let before = self.fields.len();
118 self.fields.retain(|f| f.tag != tag);
119 self.fields.len() != before
120 }
121
122 pub fn iter(&self) -> impl Iterator<Item = &TagValue> {
123 self.fields.iter()
124 }
125
126 pub(crate) fn fields(&self) -> &[TagValue] {
127 &self.fields
128 }
129
130 pub(crate) fn push_tag_value(&mut self, tv: TagValue) {
131 self.fields.push(tv);
132 }
133
134 pub(crate) fn take_fields(&mut self) -> Vec<TagValue> {
135 std::mem::take(&mut self.fields)
136 }
137
138 pub(crate) fn set_fields(&mut self, fields: Vec<TagValue>) {
139 self.fields = fields;
140 }
141
142 pub fn wire_len(&self) -> usize {
144 self.fields
145 .iter()
146 .map(|f| dec_len(f.tag) + 1 + f.value.len() + 1)
147 .sum()
148 }
149
150 pub fn write_to(&self, buf: &mut Vec<u8>) {
152 for f in &self.fields {
153 write_tag_value(buf, f.tag, &f.value);
154 }
155 }
156
157 pub fn read_groups(&self, template: &GroupTemplate) -> Result<Vec<FieldMap>, ConversionError> {
164 let Some(pos) = self.fields.iter().position(|f| f.tag == template.num_tag) else {
165 return Ok(Vec::new());
166 };
167 let declared: usize = self.get(template.num_tag)?;
168
169 let mut groups: Vec<FieldMap> = Vec::new();
170 for f in &self.fields[pos + 1..] {
171 if f.tag == template.delimiter() {
172 groups.push(FieldMap::new());
173 } else if groups.is_empty() || !template.is_member(f.tag) {
174 break;
175 }
176 match groups.last_mut() {
177 Some(g) => g.push_tag_value(f.clone()),
178 None => {
180 return Err(ConversionError::InvalidValue {
181 tag: f.tag,
182 value: String::from_utf8_lossy(&f.value).into_owned(),
183 });
184 }
185 }
186 }
187
188 if groups.len() != declared {
189 return Err(ConversionError::InvalidValue {
190 tag: template.num_tag,
191 value: declared.to_string(),
192 });
193 }
194 Ok(groups)
195 }
196
197 pub fn write_groups(&mut self, template: &GroupTemplate, groups: &[FieldMap]) {
202 self.set(template.num_tag, groups.len());
203 for g in groups {
204 debug_assert_eq!(
205 g.fields.first().map(|f| f.tag),
206 Some(template.delimiter()),
207 "group instance must start with its delimiter tag"
208 );
209 for f in &g.fields {
210 self.fields.push(f.clone());
211 }
212 }
213 }
214}
215
216#[derive(Debug, Clone)]
223pub struct GroupTemplate {
224 pub num_tag: Tag,
225 pub member_tags: Vec<Tag>,
226}
227
228impl GroupTemplate {
229 pub fn new(num_tag: Tag, member_tags: Vec<Tag>) -> Self {
230 assert!(!member_tags.is_empty(), "group template needs at least a delimiter tag");
231 Self { num_tag, member_tags }
232 }
233
234 pub fn delimiter(&self) -> Tag {
235 self.member_tags[0]
236 }
237
238 pub fn is_member(&self, tag: Tag) -> bool {
239 self.member_tags.contains(&tag)
240 }
241}
242
243pub(crate) fn write_tag_value(buf: &mut Vec<u8>, tag: Tag, value: &[u8]) {
244 buf.extend_from_slice(tag.to_string().as_bytes());
245 buf.push(b'=');
246 buf.extend_from_slice(value);
247 buf.push(crate::message::SOH);
248}
249
250fn dec_len(v: crate::message::Tag) -> usize {
251 let (mut v, mut n) = if v < 0 { (-(v as i64), 2usize) } else { (v as i64, 1) };
252 while v >= 10 {
253 v /= 10;
254 n += 1;
255 }
256 n
257}
258
259#[cfg(test)]
260mod tests {
261 use super::*;
262
263 #[test]
264 fn set_replaces_push_appends() {
265 let mut fm = FieldMap::new();
266 fm.set(55, "TSLA");
267 fm.set(54, '1');
268 fm.set(55, "AAPL");
269 assert_eq!(fm.len(), 2);
270 assert_eq!(fm.get_string(55).unwrap(), "AAPL");
271
272 fm.push(55, "MSFT");
273 assert_eq!(fm.len(), 3);
274 assert_eq!(fm.get_string(55).unwrap(), "AAPL");
276 }
277
278 #[test]
279 fn group_roundtrip() {
280 let tpl = GroupTemplate::new(267, vec![269]);
282 let mut body = FieldMap::new();
283 body.set(262, "REQ1");
284
285 let mut g1 = FieldMap::new();
286 g1.push(269, '0');
287 let mut g2 = FieldMap::new();
288 g2.push(269, '1');
289 body.write_groups(&tpl, &[g1, g2]);
290
291 assert_eq!(body.get::<usize>(267).unwrap(), 2);
292 let groups = body.read_groups(&tpl).unwrap();
293 assert_eq!(groups.len(), 2);
294 assert_eq!(groups[0].get::<char>(269).unwrap(), '0');
295 assert_eq!(groups[1].get::<char>(269).unwrap(), '1');
296 }
297
298 #[test]
299 fn group_count_mismatch_errors() {
300 let tpl = GroupTemplate::new(267, vec![269]);
301 let mut body = FieldMap::new();
302 body.set(267, 3usize);
303 body.push(269, '0');
304 assert!(body.read_groups(&tpl).is_err());
305 }
306
307 #[test]
308 fn group_terminates_on_foreign_tag() {
309 let tpl = GroupTemplate::new(267, vec![269, 270]);
310 let mut body = FieldMap::new();
311 body.set(267, 1usize);
312 body.push(269, '0');
313 body.push(270, "101.5");
314 body.push(58, "trailing field, not a member");
315 let groups = body.read_groups(&tpl).unwrap();
316 assert_eq!(groups.len(), 1);
317 assert_eq!(groups[0].len(), 2);
318 }
319}