truefix_core/group.rs
1//! Repeating-group builder.
2//!
3//! A group is a count tag (NoXxx) plus ordered entries; each entry is a [`FieldMap`] whose
4//! first field is the group delimiter. Dictionary-driven *parsing* of groups from a flat
5//! wire message is added in `truefix-dict` (Stage S4); this type lets callers *build* and
6//! *encode* groups (including nested ones) today.
7
8use crate::field_map::FieldMap;
9
10/// Supplies repeating-group structure to the dictionary-driven decoder without `truefix-core`
11/// depending on `truefix-dict` (the dictionary implements this trait). See [`crate::decode_with_groups`].
12pub trait GroupSpec {
13 /// If `count_tag` is a repeating-group count tag, return its `(delimiter, member_tags)`.
14 fn group_of(&self, count_tag: u32) -> Option<(u32, &[u32])>;
15}
16
17/// A repeating group: a count tag plus its ordered entries.
18#[derive(Debug, Clone, Default, PartialEq, Eq)]
19pub struct Group {
20 count_tag: u32,
21 entries: Vec<FieldMap>,
22 /// NEW-22 (feature 009): the count this group's `NoXxx` field declared on the wire, when
23 /// decoded from one — `None` for a group built fresh (e.g. by application code constructing
24 /// an outbound message), which always encodes `entries.len()`, same as before this field
25 /// existed. Preserves round-trip fidelity for a decoded group whose declared count didn't
26 /// match its actual entry count (previously silently "corrected" to `entries.len()` on
27 /// re-encode, discarding the wire's own original — possibly malformed — declaration).
28 declared_count: Option<i64>,
29}
30
31impl Group {
32 /// Create an empty group for the given NoXxx count tag.
33 pub fn new(count_tag: u32) -> Self {
34 Self {
35 count_tag,
36 entries: Vec::new(),
37 declared_count: None,
38 }
39 }
40
41 /// Append an entry; returns `&mut self` for chaining.
42 pub fn add_entry(&mut self, entry: FieldMap) -> &mut Self {
43 self.entries.push(entry);
44 self
45 }
46
47 /// The NoXxx count tag.
48 pub fn count_tag(&self) -> u32 {
49 self.count_tag
50 }
51
52 /// Number of entries.
53 pub fn len(&self) -> usize {
54 self.entries.len()
55 }
56
57 /// Whether the group has no entries.
58 pub fn is_empty(&self) -> bool {
59 self.entries.is_empty()
60 }
61
62 /// NEW-22 (feature 009): record the count actually declared on the wire (set by the decoder;
63 /// not exposed for outbound-only construction, which has no such thing to preserve).
64 pub(crate) fn set_declared_count(&mut self, n: i64) {
65 self.declared_count = Some(n);
66 }
67
68 /// Consume the group into `(count_tag, entries, declared_count)`.
69 pub(crate) fn into_parts(self) -> (u32, Vec<FieldMap>, Option<i64>) {
70 (self.count_tag, self.entries, self.declared_count)
71 }
72}