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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
//! An ordered collection of fields and nested repeating groups.
use crate::field::Field;
use crate::group::Group;
/// One member of a [`FieldMap`]: either a plain field or a repeating group.
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum Member {
/// A plain field.
Field(Field),
/// A repeating group: its count tag and ordered entries.
Group {
/// The NoXxx count tag.
count_tag: u32,
/// The group entries (each a `FieldMap`).
entries: Vec<FieldMap>,
/// NEW-22 (feature 009): the count declared on the wire, if decoded from one and it
/// didn't match `entries.len()` — see `Group::declared_count`'s doc.
declared_count: Option<i64>,
},
}
/// An ordered map of FIX fields (and nested groups), preserving wire order.
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct FieldMap {
members: Vec<Member>,
}
impl FieldMap {
/// Create an empty field map.
pub fn new() -> Self {
Self::default()
}
/// Append a field, preserving insertion order (used by the decoder).
pub fn add_field(&mut self, field: Field) {
self.members.push(Member::Field(field));
}
/// Set a top-level field, replacing an existing one with the same tag (in place, preserving
/// its position), else appending. If more than one field with this tag is already present
/// (NEW-81, feature 009 -- e.g. because `add_field` was used to push duplicates directly,
/// bypassing this method's usual dedup), every stale copy is removed, leaving only the new
/// value at the first occurrence's position.
pub fn set(&mut self, field: Field) {
let tag = field.tag();
let mut replaced = false;
self.members.retain_mut(|m| {
let Member::Field(existing) = m else {
return true;
};
if existing.tag() != tag {
return true;
}
if replaced {
return false;
}
*existing = field.clone();
replaced = true;
true
});
if !replaced {
self.members.push(Member::Field(field));
}
}
/// Get the first top-level field with `tag`.
pub fn get(&self, tag: u32) -> Option<&Field> {
self.members.iter().find_map(|m| match m {
Member::Field(f) if f.tag() == tag => Some(f),
_ => None,
})
}
/// Returns `true` if a top-level field with `tag` is present.
pub fn contains(&self, tag: u32) -> bool {
self.get(tag).is_some()
}
/// Append a repeating group.
pub fn add_group(&mut self, group: Group) {
let (count_tag, entries, declared_count) = group.into_parts();
self.members.push(Member::Group {
count_tag,
entries,
declared_count,
});
}
/// Get the entries of the first group with `count_tag`.
pub fn group(&self, count_tag: u32) -> Option<&[FieldMap]> {
self.members.iter().find_map(|m| match m {
Member::Group {
count_tag: ct,
entries,
..
} if *ct == count_tag => Some(entries.as_slice()),
_ => None,
})
}
/// Get one entry (by 0-based `index`) of the first group with `count_tag` (US9, feature 005,
/// FR-024/FR-025). `None` if the group doesn't exist or `index` is out of range.
pub fn get_group(&self, count_tag: u32, index: usize) -> Option<&FieldMap> {
self.group(count_tag).and_then(|entries| entries.get(index))
}
/// Replace one entry (by 0-based `index`) of the first group with `count_tag` (US9, feature
/// 005, FR-024/FR-025). No-op if the group doesn't exist or `index` is out of range.
pub fn replace_group(&mut self, count_tag: u32, index: usize, entry: FieldMap) {
if let Some(Member::Group {
count_tag: ct,
entries,
..
}) = self
.members
.iter_mut()
.find(|m| matches!(m, Member::Group { count_tag: ct, .. } if *ct == count_tag))
{
debug_assert_eq!(*ct, count_tag);
if let Some(slot) = entries.get_mut(index) {
*slot = entry;
}
}
}
/// Remove one entry (by 0-based `index`) of the first group with `count_tag` (US9, feature
/// 005, FR-024/FR-025), shifting later entries down. No-op if the group doesn't exist or
/// `index` is out of range.
pub fn remove_group(&mut self, count_tag: u32, index: usize) {
if let Some(Member::Group {
count_tag: ct,
entries,
declared_count,
}) = self
.members
.iter_mut()
.find(|m| matches!(m, Member::Group { count_tag: ct, .. } if *ct == count_tag))
{
debug_assert_eq!(*ct, count_tag);
if index < entries.len() {
entries.remove(index);
// NEW-22 (feature 009): the entry count just changed -- a stale wire-declared
// count would otherwise be re-encoded verbatim, now genuinely wrong rather than
// preserving fidelity to anything real.
*declared_count = None;
}
}
}
/// Iterate the top-level fields (skipping repeating groups), in order.
pub fn fields(&self) -> impl Iterator<Item = &Field> {
self.members.iter().filter_map(|m| match m {
Member::Field(f) => Some(f),
Member::Group { .. } => None,
})
}
/// Iterate every top-level member (feature 011, FR-001) — both plain fields and repeating
/// groups, in wire order. Unlike [`Self::fields`] (which silently skips `Member::Group`
/// entries), this is the accessor for callers that need to see a message's real structure —
/// e.g. a group whose own count tag is itself required at the message level (see
/// `truefix-dict`'s `present()`), or any caller walking a decoded message generically without
/// assuming every top-level tag is a plain field.
pub fn members(&self) -> impl Iterator<Item = MemberRef<'_>> {
self.members.iter().map(|m| match m {
Member::Field(f) => MemberRef::Field(f),
Member::Group {
count_tag,
entries,
declared_count,
} => MemberRef::Group {
count_tag: *count_tag,
entries,
declared_count: *declared_count,
},
})
}
/// Internal: ordered members, for the encoder.
pub(crate) fn raw_members(&self) -> &[Member] {
&self.members
}
}
/// A borrowed view of one [`FieldMap`] top-level member, returned by [`FieldMap::members`] —
/// either a plain field or a repeating group's count tag plus its ordered entries.
#[derive(Debug, Clone, Copy)]
pub enum MemberRef<'a> {
/// A plain field.
Field(&'a Field),
/// A repeating group: its count tag, ordered entries, and (per [`crate::Group`]'s doc) the
/// wire-declared count when it didn't match `entries.len()`.
Group {
/// The `NoXxx` count tag.
count_tag: u32,
/// The group's ordered entries.
entries: &'a [FieldMap],
/// The count declared on the wire, if decoded from one and it didn't match
/// `entries.len()` (`None` for a group built directly by application code).
declared_count: Option<i64>,
},
}