1use crate::field::Field;
2use crate::tag::{self, Tag};
3
4pub struct GroupSpec {
10 pub count_tag: Tag,
11 pub delimiter_tag: Tag,
12 pub member_tags: &'static [Tag],
13}
14
15pub const ALLOCS: GroupSpec = GroupSpec {
22 count_tag: tag::NO_ALLOCS,
23 delimiter_tag: tag::ALLOC_ACCOUNT,
24 member_tags: &[tag::ALLOC_ACCOUNT, tag::ALLOC_SHARES, tag::PROCESS_CODE],
25};
26
27pub const ORDERS: GroupSpec = GroupSpec {
29 count_tag: tag::NO_ORDERS,
30 delimiter_tag: tag::CL_ORD_ID,
31 member_tags: &[
32 tag::CL_ORD_ID,
33 tag::LIST_SEQ_NO,
34 tag::WAVE_NO,
35 tag::ACCOUNT,
36 tag::SETTLMNT_TYP,
37 tag::FUT_SETT_DATE,
38 tag::HANDL_INST,
39 tag::EXEC_INST,
40 tag::MIN_QTY,
41 tag::MAX_FLOOR,
42 tag::EX_DESTINATION,
43 tag::OPEN_CLOSE,
44 tag::COVERED_OR_UNCOVERED,
45 tag::CUSTOMER_OR_FIRM,
46 tag::MAX_SHOW,
47 tag::PRICE,
48 tag::STOP_PX,
49 tag::PEG_DIFFERENCE,
50 tag::DISCRETION_INST,
51 tag::DISCRETION_OFFSET,
52 tag::CURRENCY,
53 tag::COMPLIANCE_ID,
54 tag::SOLICITED_FLAG,
55 tag::IOI_ID,
56 tag::TIME_IN_FORCE,
57 tag::EXPIRE_TIME,
58 tag::COMMISSION,
59 tag::RULE80A,
60 tag::FOREX_REQ,
61 tag::SETTL_CURRENCY,
62 tag::ORDER_QTY,
63 tag::CASH_ORDER_QTY,
64 tag::ORD_TYPE,
65 tag::SIDE,
66 tag::LOCATE_REQD,
67 tag::TRANSACT_TIME,
68 tag::SYMBOL,
69 tag::SYMBOL_SFX,
70 tag::SECURITY_ID,
71 tag::ID_SOURCE,
72 tag::SECURITY_TYPE,
73 tag::MATURITY_MONTH_YEAR,
74 tag::MATURITY_DAY,
75 tag::PUT_OR_CALL,
76 tag::STRIKE_PRICE,
77 tag::OPT_ATTRIBUTE,
78 tag::CONTRACT_MULTIPLIER,
79 tag::COUPON_RATE,
80 tag::SECURITY_EXCHANGE,
81 tag::ISSUER,
82 tag::SECURITY_DESC,
83 tag::TEXT,
84 ],
85};
86
87pub const RPTS: GroupSpec = GroupSpec {
89 count_tag: tag::NO_RPTS,
90 delimiter_tag: tag::RPT_SEQ,
91 member_tags: &[tag::RPT_SEQ],
92};
93
94pub const DLVY_INST: GroupSpec = GroupSpec {
96 count_tag: tag::NO_DLVY_INST,
97 delimiter_tag: tag::DLVY_INST,
98 member_tags: &[tag::DLVY_INST],
99};
100
101pub const EXECS: GroupSpec = GroupSpec {
103 count_tag: tag::NO_EXECS,
104 delimiter_tag: tag::EXEC_ID,
105 member_tags: &[
106 tag::EXEC_ID,
107 tag::LAST_SHARES,
108 tag::LAST_PX,
109 tag::LAST_CAPACITY,
110 ],
111};
112
113pub const MISC_FEES: GroupSpec = GroupSpec {
115 count_tag: tag::NO_MISC_FEES,
116 delimiter_tag: tag::MISC_FEE_AMT,
117 member_tags: &[tag::MISC_FEE_AMT, tag::MISC_FEE_CURR, tag::MISC_FEE_TYPE],
118};
119
120pub const RELATED_SYM: GroupSpec = GroupSpec {
122 count_tag: tag::NO_RELATED_SYM,
123 delimiter_tag: tag::RELATD_SYM,
124 member_tags: &[
125 tag::RELATD_SYM,
126 tag::SYMBOL_SFX,
127 tag::SECURITY_ID,
128 tag::ID_SOURCE,
129 tag::SECURITY_TYPE,
130 tag::MATURITY_MONTH_YEAR,
131 tag::MATURITY_DAY,
132 tag::PUT_OR_CALL,
133 tag::STRIKE_PRICE,
134 tag::OPT_ATTRIBUTE,
135 tag::CONTRACT_MULTIPLIER,
136 tag::COUPON_RATE,
137 tag::SECURITY_EXCHANGE,
138 tag::ISSUER,
139 tag::SECURITY_DESC,
140 ],
141};
142
143pub const IOI_QUALIFIERS: GroupSpec = GroupSpec {
145 count_tag: tag::NO_IOI_QUALIFIERS,
146 delimiter_tag: tag::IOI_QUALIFIER,
147 member_tags: &[tag::IOI_QUALIFIER],
148};
149
150pub const ROUTING_IDS: GroupSpec = GroupSpec {
152 count_tag: tag::NO_ROUTING_IDS,
153 delimiter_tag: tag::ROUTING_TYPE,
154 member_tags: &[tag::ROUTING_TYPE, tag::ROUTING_ID],
155};
156
157pub const MD_ENTRY_TYPES: GroupSpec = GroupSpec {
159 count_tag: tag::NO_MD_ENTRY_TYPES,
160 delimiter_tag: tag::MD_ENTRY_TYPE,
161 member_tags: &[tag::MD_ENTRY_TYPE],
162};
163
164pub const MD_ENTRIES: GroupSpec = GroupSpec {
166 count_tag: tag::NO_MD_ENTRIES,
167 delimiter_tag: tag::MD_ENTRY_TYPE,
168 member_tags: &[
169 tag::MD_ENTRY_TYPE,
170 tag::MD_ENTRY_PX,
171 tag::MD_ENTRY_SIZE,
172 tag::MD_ENTRY_DATE,
173 tag::MD_ENTRY_TIME,
174 tag::TICK_DIRECTION,
175 tag::MD_MKT,
176 tag::QUOTE_CONDITION,
177 tag::TRADE_CONDITION,
178 tag::MD_ENTRY_ID,
179 tag::MD_UPDATE_ACTION,
180 tag::MD_ENTRY_REF_ID,
181 tag::MD_ENTRY_ORIGINATOR,
182 tag::LOCATION_ID,
183 tag::DESK_ID,
184 tag::OPEN_CLOSE_SETTLE_FLAG,
185 tag::SELLER_DAYS,
186 tag::MD_ENTRY_BUYER,
187 tag::MD_ENTRY_SELLER,
188 tag::MD_ENTRY_POSITION_NO,
189 tag::FINANCIAL_STATUS,
190 tag::CORPORATE_ACTION,
191 ],
192};
193
194pub const QUOTE_ENTRIES: GroupSpec = GroupSpec {
196 count_tag: tag::NO_QUOTE_ENTRIES,
197 delimiter_tag: tag::QUOTE_ENTRY_ID,
198 member_tags: &[
199 tag::QUOTE_ENTRY_ID,
200 tag::SYMBOL,
201 tag::SYMBOL_SFX,
202 tag::SECURITY_ID,
203 tag::ID_SOURCE,
204 tag::SECURITY_TYPE,
205 tag::MATURITY_MONTH_YEAR,
206 tag::MATURITY_DAY,
207 tag::PUT_OR_CALL,
208 tag::STRIKE_PRICE,
209 tag::OPT_ATTRIBUTE,
210 tag::CONTRACT_MULTIPLIER,
211 tag::COUPON_RATE,
212 tag::SECURITY_EXCHANGE,
213 tag::ISSUER,
214 tag::SECURITY_DESC,
215 tag::BID_PX,
216 tag::OFFER_PX,
217 tag::BID_SIZE,
218 tag::OFFER_SIZE,
219 tag::VALID_UNTIL_TIME,
220 tag::BID_SPOT_RATE,
221 tag::OFFER_SPOT_RATE,
222 tag::BID_FORWARD_POINTS,
223 tag::OFFER_FORWARD_POINTS,
224 tag::TRANSACT_TIME,
225 tag::TRADING_SESSION_ID,
226 tag::QUOTE_ENTRY_REJECT_REASON,
227 ],
228};
229
230pub const QUOTE_SETS: GroupSpec = GroupSpec {
232 count_tag: tag::NO_QUOTE_SETS,
233 delimiter_tag: tag::QUOTE_SET_ID,
234 member_tags: &[
235 tag::QUOTE_SET_ID,
236 tag::UNDERLYING_SYMBOL,
237 tag::UNDERLYING_SYMBOL_SFX,
238 tag::UNDERLYING_SECURITY_ID,
239 tag::UNDERLYING_ID_SOURCE,
240 tag::UNDERLYING_SECURITY_TYPE,
241 tag::UNDERLYING_MATURITY_MONTH_YEAR,
242 tag::UNDERLYING_MATURITY_DAY,
243 tag::UNDERLYING_PUT_OR_CALL,
244 tag::UNDERLYING_STRIKE_PRICE,
245 tag::UNDERLYING_OPT_ATTRIBUTE,
246 tag::UNDERLYING_CURRENCY,
247 tag::QUOTE_SET_VALID_UNTIL_TIME,
248 tag::TOT_QUOTE_ENTRIES,
249 tag::NO_QUOTE_ENTRIES,
250 ],
251};
252
253pub const CONTRA_BROKERS: GroupSpec = GroupSpec {
255 count_tag: tag::NO_CONTRA_BROKERS,
256 delimiter_tag: tag::CONTRA_BROKER,
257 member_tags: &[
258 tag::CONTRA_BROKER,
259 tag::CONTRA_TRADER,
260 tag::CONTRA_TRADE_QTY,
261 tag::CONTRA_TRADE_TIME,
262 ],
263};
264
265pub const MSG_TYPES: GroupSpec = GroupSpec {
267 count_tag: tag::NO_MSG_TYPES,
268 delimiter_tag: tag::REF_MSG_TYPE,
269 member_tags: &[tag::REF_MSG_TYPE, tag::MSG_DIRECTION],
270};
271
272pub const TRADING_SESSIONS: GroupSpec = GroupSpec {
274 count_tag: tag::NO_TRADING_SESSIONS,
275 delimiter_tag: tag::TRADING_SESSION_ID,
276 member_tags: &[tag::TRADING_SESSION_ID],
277};
278
279pub const BID_DESCRIPTORS: GroupSpec = GroupSpec {
281 count_tag: tag::NO_BID_DESCRIPTORS,
282 delimiter_tag: tag::BID_DESCRIPTOR_TYPE,
283 member_tags: &[
284 tag::BID_DESCRIPTOR_TYPE,
285 tag::BID_DESCRIPTOR,
286 tag::SIDE_VALUE_IND,
287 tag::LIQUIDITY_VALUE,
288 tag::LIQUIDITY_NUM_SECURITIES,
289 tag::LIQUIDITY_PCT_LOW,
290 tag::LIQUIDITY_PCT_HIGH,
291 tag::EFP_TRACKING_ERROR,
292 tag::FAIR_VALUE,
293 tag::OUTSIDE_INDEX_PCT,
294 tag::VALUE_OF_FUTURES,
295 ],
296};
297
298pub const BID_COMPONENTS: GroupSpec = GroupSpec {
300 count_tag: tag::NO_BID_COMPONENTS,
301 delimiter_tag: tag::CLEARING_FIRM,
302 member_tags: &[
303 tag::CLEARING_FIRM,
304 tag::CLEARING_ACCOUNT,
305 tag::LIQUIDITY_IND_TYPE,
306 tag::WT_AVERAGE_LIQUIDITY,
307 tag::EXCHANGE_FOR_PHYSICAL,
308 tag::OUT_MAIN_CNTRY_U_INDEX,
309 tag::CROSS_PERCENT,
310 tag::PROG_RPT_REQS,
311 tag::PROG_PERIOD_INTERVAL,
312 tag::INC_TAX_IND,
313 tag::NUM_BIDDERS,
314 tag::TRADE_TYPE,
315 tag::BASIS_PX_TYPE,
316 tag::COUNTRY,
317 tag::SIDE,
318 tag::PRICE,
319 tag::PRICE_TYPE,
320 tag::FAIR_VALUE,
321 ],
322};
323
324pub const STRIKES: GroupSpec = GroupSpec {
326 count_tag: tag::NO_STRIKES,
327 delimiter_tag: tag::SYMBOL,
328 member_tags: &[
329 tag::SYMBOL,
330 tag::SYMBOL_SFX,
331 tag::SECURITY_ID,
332 tag::ID_SOURCE,
333 tag::SECURITY_TYPE,
334 tag::MATURITY_MONTH_YEAR,
335 tag::MATURITY_DAY,
336 tag::PUT_OR_CALL,
337 tag::STRIKE_PRICE,
338 tag::OPT_ATTRIBUTE,
339 tag::CONTRACT_MULTIPLIER,
340 tag::COUPON_RATE,
341 tag::SECURITY_EXCHANGE,
342 tag::ISSUER,
343 tag::SECURITY_DESC,
344 ],
345};
346
347pub const PARTY_IDS: GroupSpec = GroupSpec {
354 count_tag: tag::NO_PARTY_IDS,
355 delimiter_tag: tag::PARTY_ID,
356 member_tags: &[
357 tag::PARTY_ID,
358 tag::PARTY_ID_SOURCE,
359 tag::PARTY_ROLE,
360 tag::PARTY_SUB_ID,
361 ],
362};
363
364pub const SECURITY_ALT_IDS: GroupSpec = GroupSpec {
366 count_tag: tag::NO_SECURITY_ALT_ID,
367 delimiter_tag: tag::SECURITY_ALT_ID,
368 member_tags: &[tag::SECURITY_ALT_ID, tag::SECURITY_ALT_ID_SOURCE],
369};
370
371pub const UNDERLYING_SECURITY_ALT_IDS: GroupSpec = GroupSpec {
373 count_tag: tag::NO_UNDERLYING_SECURITY_ALT_ID,
374 delimiter_tag: tag::UNDERLYING_SECURITY_ALT_ID,
375 member_tags: &[
376 tag::UNDERLYING_SECURITY_ALT_ID,
377 tag::UNDERLYING_SECURITY_ALT_ID_SOURCE,
378 ],
379};
380
381pub const REGIST_DTLS: GroupSpec = GroupSpec {
383 count_tag: tag::NO_REGIST_DTLS,
384 delimiter_tag: tag::MAILING_DTLS,
385 member_tags: &[
386 tag::MAILING_DTLS,
387 tag::INVESTOR_COUNTRY_OF_RESIDENCE,
388 tag::MAILING_INST,
389 tag::REGIST_DTLS,
390 tag::REGIST_EMAIL,
391 tag::DISTRIB_PERCENTAGE,
392 tag::REGIST_ID,
393 tag::REGIST_TRANS_TYPE,
394 tag::OWNER_TYPE,
395 tag::NO_DISTRIB_INSTS,
396 tag::DISTRIB_PAYMENT_METHOD,
397 tag::CASH_DISTRIB_CURR,
398 tag::CASH_DISTRIB_AGENT_NAME,
399 tag::CASH_DISTRIB_AGENT_CODE,
400 tag::CASH_DISTRIB_AGENT_ACCT_NUMBER,
401 tag::CASH_DISTRIB_PAY_REF,
402 tag::CASH_DISTRIB_AGENT_ACCT_NAME,
403 ],
404};
405
406pub const DISTRIB_INSTS: GroupSpec = GroupSpec {
408 count_tag: tag::NO_DISTRIB_INSTS,
409 delimiter_tag: tag::DISTRIB_PAYMENT_METHOD,
410 member_tags: &[
411 tag::DISTRIB_PAYMENT_METHOD,
412 tag::DISTRIB_PERCENTAGE,
413 tag::CASH_DISTRIB_CURR,
414 tag::CASH_DISTRIB_AGENT_NAME,
415 tag::CASH_DISTRIB_AGENT_CODE,
416 tag::CASH_DISTRIB_AGENT_ACCT_NUMBER,
417 tag::CASH_DISTRIB_PAY_REF,
418 tag::CASH_DISTRIB_AGENT_ACCT_NAME,
419 ],
420};
421
422pub const CONT_AMTS: GroupSpec = GroupSpec {
424 count_tag: tag::NO_CONT_AMTS,
425 delimiter_tag: tag::CONT_AMT_TYPE,
426 member_tags: &[tag::CONT_AMT_TYPE, tag::CONT_AMT_VALUE, tag::CONT_AMT_CURR],
427};
428
429pub const NESTED_PARTY_IDS: GroupSpec = GroupSpec {
431 count_tag: tag::NO_NESTED_PARTY_IDS,
432 delimiter_tag: tag::NESTED_PARTY_ID,
433 member_tags: &[
434 tag::NESTED_PARTY_ID,
435 tag::NESTED_PARTY_ID_SOURCE,
436 tag::NESTED_PARTY_ROLE,
437 tag::NESTED_PARTY_SUB_ID,
438 ],
439};
440
441pub const SIDES: GroupSpec = GroupSpec {
443 count_tag: tag::NO_SIDES,
444 delimiter_tag: tag::SIDE,
445 member_tags: &[
446 tag::SIDE,
447 tag::ORDER_ID,
448 tag::SECONDARY_ORDER_ID,
449 tag::CL_ORD_ID,
450 tag::SECONDARY_CL_ORD_ID,
451 tag::LIST_ID,
452 tag::ACCOUNT,
453 tag::ACCT_ID_SOURCE,
454 tag::ACCOUNT_TYPE,
455 tag::PROCESS_CODE,
456 tag::ODD_LOT,
457 tag::NO_CLEARING_INSTRUCTIONS,
458 tag::CLEARING_INSTRUCTION,
459 tag::CLEARING_FEE_INDICATOR,
460 tag::TRADE_INPUT_SOURCE,
461 tag::TRADE_INPUT_DEVICE,
462 tag::ORDER_INPUT_DEVICE,
463 tag::CURRENCY,
464 tag::COMPLIANCE_ID,
465 tag::SOLICITED_FLAG,
466 tag::ORDER_CAPACITY,
467 tag::ORDER_RESTRICTIONS,
468 tag::CUST_ORDER_CAPACITY,
469 tag::ORD_TYPE,
470 tag::EXEC_INST,
471 tag::TRANS_BKD_TIME,
472 tag::TRADING_SESSION_ID,
473 tag::TRADING_SESSION_SUB_ID,
474 tag::COMMISSION,
475 tag::COMM_TYPE,
476 tag::COMM_CURRENCY,
477 tag::FUND_RENEW_WAIV,
478 tag::GROSS_TRADE_AMT,
479 tag::NUM_DAYS_INTEREST,
480 tag::EX_DESTINATION,
481 tag::ACCRUED_INTEREST_RATE,
482 tag::ACCRUED_INTEREST_AMT,
483 tag::INTEREST_AT_MATURITY,
484 tag::END_ACCRUED_INTEREST_AMT,
485 tag::START_CASH,
486 tag::END_CASH,
487 tag::NET_MONEY,
488 tag::SETTL_CURR_AMT,
489 tag::SETTL_CURRENCY,
490 tag::SETTL_CURR_FX_RATE,
491 tag::SETTL_CURR_FX_RATE_CALC,
492 tag::POSITION_EFFECT,
493 tag::TEXT,
494 tag::ENCODED_TEXT_LEN,
495 tag::ENCODED_TEXT,
496 tag::SIDE_MULTI_LEG_REPORTING_TYPE,
497 tag::NO_CONT_AMTS,
498 tag::CONT_AMT_TYPE,
499 tag::CONT_AMT_VALUE,
500 tag::CONT_AMT_CURR,
501 tag::NO_MISC_FEES,
502 tag::MISC_FEE_AMT,
503 tag::MISC_FEE_CURR,
504 tag::MISC_FEE_TYPE,
505 tag::MISC_FEE_BASIS,
506 tag::EXCHANGE_RULE,
507 tag::TRADE_ALLOC_INDICATOR,
508 tag::PREALLOC_METHOD,
509 tag::ALLOC_ID,
510 tag::NO_ALLOCS,
511 tag::ALLOC_ACCOUNT,
512 tag::ALLOC_ACCT_ID_SOURCE,
513 tag::ALLOC_SETTL_CURRENCY,
514 tag::INDIVIDUAL_ALLOC_ID,
515 tag::ALLOC_SHARES,
516 ],
517};
518
519pub const SECURITY_TYPES: GroupSpec = GroupSpec {
521 count_tag: tag::NO_SECURITY_TYPES,
522 delimiter_tag: tag::SECURITY_TYPE,
523 member_tags: &[tag::SECURITY_TYPE, tag::PRODUCT, tag::CFI_CODE],
524};
525
526pub const AFFECTED_ORDERS: GroupSpec = GroupSpec {
528 count_tag: tag::NO_AFFECTED_ORDERS,
529 delimiter_tag: tag::AFFECTED_ORDER_ID,
530 member_tags: &[tag::AFFECTED_ORDER_ID, tag::AFFECTED_SECONDARY_ORDER_ID],
531};
532
533pub const LEGS: GroupSpec = GroupSpec {
535 count_tag: tag::NO_LEGS,
536 delimiter_tag: tag::LEG_SYMBOL,
537 member_tags: &[
538 tag::LEG_SYMBOL,
539 tag::LEG_SYMBOL_SFX,
540 tag::LEG_SECURITY_ID,
541 tag::LEG_SECURITY_ID_SOURCE,
542 tag::NO_LEG_SECURITY_ALT_ID,
543 tag::LEG_SECURITY_ALT_ID,
544 tag::LEG_SECURITY_ALT_ID_SOURCE,
545 tag::LEG_PRODUCT,
546 tag::LEG_CFI_CODE,
547 tag::LEG_SECURITY_TYPE,
548 tag::LEG_MATURITY_MONTH_YEAR,
549 tag::LEG_MATURITY_DATE,
550 tag::LEG_STRIKE_PRICE,
551 tag::LEG_OPT_ATTRIBUTE,
552 tag::LEG_CONTRACT_MULTIPLIER,
553 tag::LEG_COUPON_RATE,
554 tag::LEG_SECURITY_EXCHANGE,
555 tag::LEG_ISSUER,
556 tag::ENCODED_LEG_ISSUER_LEN,
557 tag::ENCODED_LEG_ISSUER,
558 tag::LEG_SECURITY_DESC,
559 tag::ENCODED_LEG_SECURITY_DESC_LEN,
560 tag::ENCODED_LEG_SECURITY_DESC,
561 tag::LEG_RATIO_QTY,
562 tag::LEG_SIDE,
563 tag::LEG_CURRENCY,
564 tag::LEG_COUNTRY_OF_ISSUE,
565 tag::LEG_STATE_OR_PROVINCE_OF_ISSUE,
566 tag::LEG_LOCALE_OF_ISSUE,
567 tag::LEG_INSTR_REGISTRY,
568 tag::LEG_DATED_DATE,
569 tag::LEG_POOL,
570 tag::LEG_CONTRACT_SETTL_MONTH,
571 tag::LEG_INTEREST_ACCRUAL_DATE,
572 tag::LEG_QTY,
573 tag::LEG_SWAP_TYPE,
574 tag::NO_LEG_STIPULATIONS,
575 tag::LEG_STIPULATION_TYPE,
576 tag::LEG_STIPULATION_VALUE,
577 tag::LEG_POSITION_EFFECT,
578 tag::LEG_COVERED_OR_UNCOVERED,
579 tag::LEG_PRICE,
580 tag::LEG_SETTL_TYPE,
581 tag::LEG_SETTL_DATE,
582 tag::LEG_LAST_PX,
583 tag::LEG_REF_ID,
584 ],
585};
586
587pub const UNDERLYINGS: GroupSpec = GroupSpec {
589 count_tag: tag::NO_UNDERLYINGS,
590 delimiter_tag: tag::UNDERLYING_SYMBOL,
591 member_tags: &[
592 tag::UNDERLYING_SYMBOL,
593 tag::UNDERLYING_SYMBOL_SFX,
594 tag::UNDERLYING_SECURITY_ID,
595 tag::UNDERLYING_ID_SOURCE,
596 tag::UNDERLYING_SECURITY_TYPE,
597 tag::UNDERLYING_MATURITY_MONTH_YEAR,
598 tag::UNDERLYING_MATURITY_DATE,
599 tag::UNDERLYING_PUT_OR_CALL,
600 tag::UNDERLYING_STRIKE_PRICE,
601 tag::UNDERLYING_OPT_ATTRIBUTE,
602 tag::UNDERLYING_CONTRACT_MULTIPLIER,
603 tag::UNDERLYING_COUPON_RATE,
604 tag::UNDERLYING_SECURITY_EXCHANGE,
605 tag::UNDERLYING_ISSUER,
606 tag::ENCODED_UNDERLYING_ISSUER_LEN,
607 tag::ENCODED_UNDERLYING_ISSUER,
608 tag::UNDERLYING_SECURITY_DESC,
609 tag::ENCODED_UNDERLYING_SECURITY_DESC_LEN,
610 tag::ENCODED_UNDERLYING_SECURITY_DESC,
611 tag::UNDERLYING_COUPON_PAYMENT_DATE,
612 tag::UNDERLYING_ISSUE_DATE,
613 tag::UNDERLYING_REPO_COLLATERAL_SECURITY_TYPE,
614 tag::UNDERLYING_REPURCHASE_TERM,
615 tag::UNDERLYING_REPURCHASE_RATE,
616 tag::UNDERLYING_FACTOR,
617 tag::UNDERLYING_CREDIT_RATING,
618 tag::UNDERLYING_INSTR_REGISTRY,
619 tag::UNDERLYING_COUNTRY_OF_ISSUE,
620 tag::UNDERLYING_STATE_OR_PROVINCE_OF_ISSUE,
621 tag::UNDERLYING_LOCALE_OF_ISSUE,
622 tag::UNDERLYING_REDEMPTION_DATE,
623 tag::UNDERLYING_STRIKE_CURRENCY,
624 tag::UNDERLYING_SECURITY_SUB_TYPE,
625 tag::UNDERLYING_PRODUCT,
626 tag::UNDERLYING_CFI_CODE,
627 tag::UNDERLYING_CP_PROGRAM,
628 tag::UNDERLYING_CP_REG_TYPE,
629 tag::UNDERLYING_LAST_PX,
630 tag::UNDERLYING_LAST_QTY,
631 tag::UNDERLYING_QTY,
632 tag::UNDERLYING_SETTL_PRICE,
633 tag::UNDERLYING_SETTL_PRICE_TYPE,
634 tag::UNDERLYING_DIRTY_PRICE,
635 tag::UNDERLYING_END_PRICE,
636 tag::UNDERLYING_START_VALUE,
637 tag::UNDERLYING_CURRENT_VALUE,
638 tag::UNDERLYING_END_VALUE,
639 tag::NO_UNDERLYING_SECURITY_ALT_ID,
640 tag::UNDERLYING_SECURITY_ALT_ID,
641 tag::UNDERLYING_SECURITY_ALT_ID_SOURCE,
642 tag::UNDERLYING_STIP_TYPE,
643 tag::UNDERLYING_STIP_VALUE,
644 ],
645};
646
647pub const POSITIONS: GroupSpec = GroupSpec {
649 count_tag: tag::NO_POSITIONS,
650 delimiter_tag: tag::POS_TYPE,
651 member_tags: &[
652 tag::POS_TYPE,
653 tag::LONG_QTY,
654 tag::SHORT_QTY,
655 tag::POS_QTY_STATUS,
656 ],
657};
658
659pub const QUOTE_QUALIFIERS: GroupSpec = GroupSpec {
661 count_tag: tag::NO_QUOTE_QUALIFIERS,
662 delimiter_tag: tag::QUOTE_QUALIFIER,
663 member_tags: &[tag::QUOTE_QUALIFIER],
664};
665
666pub const POS_AMTS: GroupSpec = GroupSpec {
668 count_tag: tag::NO_POS_AMT,
669 delimiter_tag: tag::POS_AMT_TYPE,
670 member_tags: &[tag::POS_AMT_TYPE, tag::POS_AMT],
671};
672
673pub const NESTED2_PARTY_IDS: GroupSpec = GroupSpec {
675 count_tag: tag::NO_NESTED2_PARTY_IDS,
676 delimiter_tag: tag::NESTED2_PARTY_ID,
677 member_tags: &[
678 tag::NESTED2_PARTY_ID,
679 tag::NESTED2_PARTY_ID_SOURCE,
680 tag::NESTED2_PARTY_ROLE,
681 tag::NESTED2_PARTY_SUB_ID,
682 ],
683};
684
685pub const TRD_REG_TIMESTAMPS: GroupSpec = GroupSpec {
687 count_tag: tag::NO_TRD_REG_TIMESTAMPS,
688 delimiter_tag: tag::TRD_REG_TIMESTAMP,
689 member_tags: &[
690 tag::TRD_REG_TIMESTAMP,
691 tag::TRD_REG_TIMESTAMP_TYPE,
692 tag::TRD_REG_TIMESTAMP_ORIGIN,
693 ],
694};
695
696pub const SETTL_INST: GroupSpec = GroupSpec {
698 count_tag: tag::NO_SETTL_INST,
699 delimiter_tag: tag::SETTL_INST_ID,
700 member_tags: &[
701 tag::SETTL_INST_ID,
702 tag::SETTL_INST_TRANS_TYPE,
703 tag::SETTL_INST_REF_ID,
704 tag::SETTL_INST_MODE,
705 tag::SETTL_INST_SOURCE,
706 tag::SECURITY_ID,
707 tag::SIDE,
708 tag::TRANSACT_TIME,
709 tag::EFFECTIVE_TIME,
710 ],
711};
712
713pub const SETTL_PARTY_IDS: GroupSpec = GroupSpec {
715 count_tag: tag::NO_SETTL_PARTY_IDS,
716 delimiter_tag: tag::SETTL_PARTY_ID,
717 member_tags: &[
718 tag::SETTL_PARTY_ID,
719 tag::SETTL_PARTY_ID_SOURCE,
720 tag::SETTL_PARTY_ROLE,
721 tag::SETTL_PARTY_SUB_ID,
722 tag::SETTL_PARTY_SUB_ID_TYPE,
723 ],
724};
725
726pub const PARTY_SUB_IDS: GroupSpec = GroupSpec {
728 count_tag: tag::NO_PARTY_SUB_IDS,
729 delimiter_tag: tag::PARTY_SUB_ID,
730 member_tags: &[tag::PARTY_SUB_ID, tag::PARTY_SUB_ID_TYPE],
731};
732
733pub const NESTED_PARTY_SUB_IDS: GroupSpec = GroupSpec {
735 count_tag: tag::NO_NESTED_PARTY_SUB_IDS,
736 delimiter_tag: tag::NESTED_PARTY_SUB_ID,
737 member_tags: &[tag::NESTED_PARTY_SUB_ID, tag::NESTED_PARTY_SUB_ID_TYPE],
738};
739
740pub const NESTED2_PARTY_SUB_IDS: GroupSpec = GroupSpec {
742 count_tag: tag::NO_NESTED2_PARTY_SUB_IDS,
743 delimiter_tag: tag::NESTED2_PARTY_SUB_ID,
744 member_tags: &[tag::NESTED2_PARTY_SUB_ID, tag::NESTED2_PARTY_SUB_ID_TYPE],
745};
746
747pub const ALT_MD_SOURCES: GroupSpec = GroupSpec {
749 count_tag: tag::NO_ALT_MD_SOURCE,
750 delimiter_tag: tag::ALT_MD_SOURCE_ID,
751 member_tags: &[tag::ALT_MD_SOURCE_ID],
752};
753
754pub const CAPACITIES: GroupSpec = GroupSpec {
756 count_tag: tag::NO_CAPACITIES,
757 delimiter_tag: tag::ORDER_CAPACITY,
758 member_tags: &[tag::ORDER_CAPACITY, tag::ORDER_CAPACITY_QTY],
759};
760
761pub const EVENTS: GroupSpec = GroupSpec {
763 count_tag: tag::NO_EVENTS,
764 delimiter_tag: tag::EVENT_TYPE,
765 member_tags: &[
766 tag::EVENT_TYPE,
767 tag::EVENT_DATE,
768 tag::EVENT_PX,
769 tag::EVENT_TEXT,
770 ],
771};
772
773pub const INSTR_ATTRIB: GroupSpec = GroupSpec {
775 count_tag: tag::NO_INSTR_ATTRIB,
776 delimiter_tag: tag::INSTR_ATTRIB_TYPE,
777 member_tags: &[tag::INSTR_ATTRIB_TYPE, tag::INSTR_ATTRIB_VALUE],
778};
779
780pub const UNDERLYING_STIPS: GroupSpec = GroupSpec {
782 count_tag: tag::NO_UNDERLYING_STIPS,
783 delimiter_tag: tag::UNDERLYING_STIP_TYPE,
784 member_tags: &[tag::UNDERLYING_STIP_TYPE, tag::UNDERLYING_STIP_VALUE],
785};
786
787pub const TRADES: GroupSpec = GroupSpec {
789 count_tag: tag::NO_TRADES,
790 delimiter_tag: tag::TRADE_REPORT_ID,
791 member_tags: &[tag::TRADE_REPORT_ID, tag::SECONDARY_TRADE_REPORT_ID],
792};
793
794pub const COMP_IDS: GroupSpec = GroupSpec {
796 count_tag: tag::NO_COMP_IDS,
797 delimiter_tag: tag::REF_COMP_ID,
798 member_tags: &[
799 tag::REF_COMP_ID,
800 tag::REF_SUB_ID,
801 tag::STATUS_VALUE,
802 tag::STATUS_TEXT,
803 ],
804};
805
806pub const COLL_INQUIRY_QUALIFIERS: GroupSpec = GroupSpec {
808 count_tag: tag::NO_COLL_INQUIRY_QUALIFIER,
809 delimiter_tag: tag::COLL_INQUIRY_QUALIFIER,
810 member_tags: &[tag::COLL_INQUIRY_QUALIFIER],
811};
812
813pub const NESTED3_PARTY_IDS: GroupSpec = GroupSpec {
815 count_tag: tag::NO_NESTED3_PARTY_IDS,
816 delimiter_tag: tag::NESTED3_PARTY_ID,
817 member_tags: &[
818 tag::NESTED3_PARTY_ID,
819 tag::NESTED3_PARTY_ID_SOURCE,
820 tag::NESTED3_PARTY_ROLE,
821 tag::NESTED3_PARTY_SUB_ID,
822 tag::NESTED3_PARTY_SUB_ID_TYPE,
823 ],
824};
825
826pub const LEG_SECURITY_ALT_IDS: GroupSpec = GroupSpec {
828 count_tag: tag::NO_LEG_SECURITY_ALT_ID,
829 delimiter_tag: tag::LEG_SECURITY_ALT_ID,
830 member_tags: &[tag::LEG_SECURITY_ALT_ID, tag::LEG_SECURITY_ALT_ID_SOURCE],
831};
832
833pub const LEG_STIPULATIONS: GroupSpec = GroupSpec {
835 count_tag: tag::NO_LEG_STIPULATIONS,
836 delimiter_tag: tag::LEG_STIPULATION_TYPE,
837 member_tags: &[tag::LEG_STIPULATION_TYPE, tag::LEG_STIPULATION_VALUE],
838};
839
840pub const LEG_ALLOCS: GroupSpec = GroupSpec {
842 count_tag: tag::NO_LEG_ALLOCS,
843 delimiter_tag: tag::LEG_ALLOC_ACCOUNT,
844 member_tags: &[
845 tag::LEG_ALLOC_ACCOUNT,
846 tag::LEG_INDIVIDUAL_ALLOC_ID,
847 tag::LEG_ALLOC_QTY,
848 tag::LEG_ALLOC_ACCT_ID_SOURCE,
849 tag::LEG_SETTL_CURRENCY,
850 ],
851};
852
853pub const HOPS: GroupSpec = GroupSpec {
855 count_tag: tag::NO_HOPS,
856 delimiter_tag: tag::HOP_COMP_ID,
857 member_tags: &[tag::HOP_COMP_ID, tag::HOP_SENDING_TIME, tag::HOP_REF_ID],
858};
859
860pub const CLEARING_INSTRUCTIONS: GroupSpec = GroupSpec {
862 count_tag: tag::NO_CLEARING_INSTRUCTIONS,
863 delimiter_tag: tag::CLEARING_INSTRUCTION,
864 member_tags: &[tag::CLEARING_INSTRUCTION],
865};
866
867pub const FIX44_GROUPS: &[&GroupSpec] = &[
873 &ALLOCS,
875 &ORDERS,
876 &RPTS,
877 &DLVY_INST,
878 &EXECS,
879 &MISC_FEES,
880 &RELATED_SYM,
881 &IOI_QUALIFIERS,
882 &ROUTING_IDS,
883 &MD_ENTRY_TYPES,
884 &MD_ENTRIES,
885 "E_ENTRIES,
886 "E_SETS,
887 &CONTRA_BROKERS,
888 &MSG_TYPES,
889 &TRADING_SESSIONS,
890 &BID_DESCRIPTORS,
891 &BID_COMPONENTS,
892 &STRIKES,
893 &PARTY_IDS,
895 &SECURITY_ALT_IDS,
896 &UNDERLYING_SECURITY_ALT_IDS,
897 ®IST_DTLS,
898 &DISTRIB_INSTS,
899 &CONT_AMTS,
900 &NESTED_PARTY_IDS,
901 &SIDES,
902 &SECURITY_TYPES,
903 &AFFECTED_ORDERS,
904 &LEGS,
905 &UNDERLYINGS,
906 &POSITIONS,
907 "E_QUALIFIERS,
908 &POS_AMTS,
909 &NESTED2_PARTY_IDS,
910 &TRD_REG_TIMESTAMPS,
911 &SETTL_INST,
912 &SETTL_PARTY_IDS,
913 &PARTY_SUB_IDS,
914 &NESTED_PARTY_SUB_IDS,
915 &NESTED2_PARTY_SUB_IDS,
916 &ALT_MD_SOURCES,
917 &CAPACITIES,
918 &EVENTS,
919 &INSTR_ATTRIB,
920 &UNDERLYING_STIPS,
921 &TRADES,
922 &COMP_IDS,
923 &COLL_INQUIRY_QUALIFIERS,
924 &NESTED3_PARTY_IDS,
925 &LEG_SECURITY_ALT_IDS,
926 &LEG_STIPULATIONS,
927 &LEG_ALLOCS,
928 &HOPS,
929 &CLEARING_INSTRUCTIONS,
930];
931
932pub const FIX42_GROUPS: &[&GroupSpec] = &[
934 &ALLOCS,
935 &ORDERS,
936 &RPTS,
937 &DLVY_INST,
938 &EXECS,
939 &MISC_FEES,
940 &RELATED_SYM,
941 &IOI_QUALIFIERS,
942 &ROUTING_IDS,
943 &MD_ENTRY_TYPES,
944 &MD_ENTRIES,
945 "E_ENTRIES,
946 "E_SETS,
947 &CONTRA_BROKERS,
948 &MSG_TYPES,
949 &TRADING_SESSIONS,
950 &BID_DESCRIPTORS,
951 &BID_COMPONENTS,
952 &STRIKES,
953];
954
955#[derive(Debug, Clone, Copy)]
965pub struct Group<'a> {
966 pub(crate) buf: &'a [u8],
967 pub(crate) offsets: &'a [(Tag, u32, u32)],
968}
969
970impl<'a> Group<'a> {
971 #[inline]
973 pub fn len(&self) -> usize {
974 self.offsets.len()
975 }
976
977 #[inline]
979 pub fn is_empty(&self) -> bool {
980 self.offsets.is_empty()
981 }
982
983 #[inline]
985 pub fn field(&self, index: usize) -> Field<'a> {
986 let (tag, start, end) = self.offsets[index];
987 Field {
988 tag,
989 value: &self.buf[start as usize..end as usize],
990 }
991 }
992
993 #[inline]
995 pub fn fields(&self) -> impl Iterator<Item = Field<'a>> + '_ {
996 self.offsets.iter().map(move |&(tag, start, end)| Field {
997 tag,
998 value: &self.buf[start as usize..end as usize],
999 })
1000 }
1001
1002 #[inline]
1004 pub fn find(&self, tag: Tag) -> Option<Field<'a>> {
1005 self.offsets
1006 .iter()
1007 .find(|&&(t, _, _)| t == tag)
1008 .map(|&(t, start, end)| Field {
1009 tag: t,
1010 value: &self.buf[start as usize..end as usize],
1011 })
1012 }
1013
1014 #[inline]
1022 pub fn groups(&self, spec: &GroupSpec) -> GroupIter<'a> {
1023 let pos = self
1024 .offsets
1025 .iter()
1026 .position(|&(t, _, _)| t == spec.count_tag);
1027
1028 let (count, remaining) = match pos {
1029 None => (0, &[][..]),
1030 Some(i) => {
1031 let (_, start, end) = self.offsets[i];
1032 let count = parse_count(&self.buf[start as usize..end as usize]);
1033 let after = &self.offsets[i + 1..];
1034 (count, after)
1035 }
1036 };
1037
1038 GroupIter {
1039 buf: self.buf,
1040 remaining,
1041 delimiter_tag: spec.delimiter_tag,
1042 count,
1043 emitted: 0,
1044 }
1045 }
1046}
1047
1048pub struct GroupIter<'a> {
1053 pub(crate) buf: &'a [u8],
1054 pub(crate) remaining: &'a [(Tag, u32, u32)],
1056 pub(crate) delimiter_tag: Tag,
1057 pub(crate) count: usize,
1058 pub(crate) emitted: usize,
1059}
1060
1061impl<'a> Iterator for GroupIter<'a> {
1062 type Item = Group<'a>;
1063
1064 fn next(&mut self) -> Option<Group<'a>> {
1065 if self.emitted >= self.count || self.remaining.is_empty() {
1066 return None;
1067 }
1068
1069 let start = self.remaining;
1070
1071 let end_offset = start
1074 .iter()
1075 .enumerate()
1076 .skip(1) .find(|&(_, &(t, _, _))| t == self.delimiter_tag)
1078 .map(|(i, _)| i)
1079 .unwrap_or(start.len());
1080
1081 let instance_offsets = &start[..end_offset];
1082 self.remaining = &start[end_offset..];
1083 self.emitted += 1;
1084
1085 Some(Group {
1086 buf: self.buf,
1087 offsets: instance_offsets,
1088 })
1089 }
1090
1091 fn size_hint(&self) -> (usize, Option<usize>) {
1092 let left = self.count.saturating_sub(self.emitted);
1093 (left, Some(left))
1094 }
1095}
1096
1097pub(crate) fn parse_count(bytes: &[u8]) -> usize {
1103 let mut n: usize = 0;
1104 for &b in bytes {
1105 if !b.is_ascii_digit() {
1106 return 0;
1107 }
1108 n = n.wrapping_mul(10).wrapping_add((b - b'0') as usize);
1109 }
1110 n
1111}
1112
1113#[cfg(test)]
1118mod tests {
1119 use super::*;
1120 use crate::decoder::Decoder;
1121 use crate::tag;
1122
1123 fn fix(s: &str) -> Vec<u8> {
1125 s.bytes()
1126 .map(|b| if b == b'|' { 0x01 } else { b })
1127 .collect()
1128 }
1129
1130 #[test]
1135 fn parse_count_normal() {
1136 assert_eq!(parse_count(b"3"), 3);
1137 assert_eq!(parse_count(b"10"), 10);
1138 assert_eq!(parse_count(b"0"), 0);
1139 }
1140
1141 #[test]
1142 fn parse_count_invalid_returns_zero() {
1143 assert_eq!(parse_count(b""), 0);
1144 assert_eq!(parse_count(b"abc"), 0);
1145 assert_eq!(parse_count(b"1a"), 0);
1146 }
1147
1148 #[test]
1153 fn single_group_single_instance() {
1154 let raw = fix("136=1|137=10.5|138=USD|139=1|");
1156 let mut dec = Decoder::new();
1157 let msg = dec.decode(&raw).unwrap();
1158
1159 let mut iter = msg.groups(&MISC_FEES);
1160 let g = iter.next().expect("expected one instance");
1161 assert_eq!(g.len(), 3);
1162 assert_eq!(g.field(0).tag, tag::MISC_FEE_AMT);
1163 assert_eq!(g.field(0).value, b"10.5");
1164 assert_eq!(g.field(1).tag, tag::MISC_FEE_CURR);
1165 assert_eq!(g.field(1).value, b"USD");
1166 assert_eq!(g.field(2).tag, tag::MISC_FEE_TYPE);
1167 assert_eq!(g.field(2).value, b"1");
1168 assert!(iter.next().is_none());
1169 }
1170
1171 #[test]
1176 fn single_group_two_instances() {
1177 let raw = fix("136=2|137=10.5|138=USD|139=1|137=5.0|138=EUR|139=2|");
1179 let mut dec = Decoder::new();
1180 let msg = dec.decode(&raw).unwrap();
1181
1182 let instances: Vec<_> = msg.groups(&MISC_FEES).collect();
1183 assert_eq!(instances.len(), 2);
1184
1185 let g1 = &instances[0];
1186 assert_eq!(g1.find(tag::MISC_FEE_AMT).unwrap().value, b"10.5");
1187 assert_eq!(g1.find(tag::MISC_FEE_CURR).unwrap().value, b"USD");
1188
1189 let g2 = &instances[1];
1190 assert_eq!(g2.find(tag::MISC_FEE_AMT).unwrap().value, b"5.0");
1191 assert_eq!(g2.find(tag::MISC_FEE_CURR).unwrap().value, b"EUR");
1192 }
1193
1194 #[test]
1199 fn group_count_zero_empty_iter() {
1200 let raw = fix("136=0|35=D|");
1201 let mut dec = Decoder::new();
1202 let msg = dec.decode(&raw).unwrap();
1203
1204 let mut iter = msg.groups(&MISC_FEES);
1205 assert!(iter.next().is_none());
1206 }
1207
1208 #[test]
1213 fn group_count_tag_absent_empty_iter() {
1214 let raw = fix("35=D|49=SENDER|56=TARGET|");
1215 let mut dec = Decoder::new();
1216 let msg = dec.decode(&raw).unwrap();
1217
1218 let mut iter = msg.groups(&MISC_FEES);
1219 assert!(iter.next().is_none());
1220 }
1221
1222 #[test]
1227 fn group_fields_iter() {
1228 let raw = fix("136=1|137=9.99|138=GBP|139=3|");
1229 let mut dec = Decoder::new();
1230 let msg = dec.decode(&raw).unwrap();
1231
1232 let g = msg.groups(&MISC_FEES).next().unwrap();
1233 let tags: Vec<Tag> = g.fields().map(|f| f.tag).collect();
1234 assert_eq!(
1235 tags,
1236 vec![tag::MISC_FEE_AMT, tag::MISC_FEE_CURR, tag::MISC_FEE_TYPE]
1237 );
1238 }
1239
1240 #[test]
1241 fn group_find_present_and_absent() {
1242 let raw = fix("136=1|137=9.99|138=GBP|");
1243 let mut dec = Decoder::new();
1244 let msg = dec.decode(&raw).unwrap();
1245
1246 let g = msg.groups(&MISC_FEES).next().unwrap();
1247 assert!(g.find(tag::MISC_FEE_AMT).is_some());
1248 assert!(g.find(tag::MISC_FEE_TYPE).is_none()); }
1250
1251 #[test]
1256 fn group_iter_size_hint() {
1257 let raw = fix("136=3|137=1.0|137=2.0|137=3.0|");
1258 let mut dec = Decoder::new();
1259 let msg = dec.decode(&raw).unwrap();
1260
1261 let mut iter = msg.groups(&MISC_FEES);
1262 assert_eq!(iter.size_hint(), (3, Some(3)));
1263 iter.next();
1264 assert_eq!(iter.size_hint(), (2, Some(2)));
1265 iter.next();
1266 assert_eq!(iter.size_hint(), (1, Some(1)));
1267 iter.next();
1268 assert_eq!(iter.size_hint(), (0, Some(0)));
1269 }
1270
1271 #[test]
1276 fn md_entries_two_instances() {
1277 let raw = fix("262=REQ1|268=2|269=0|270=100.50|271=500|269=1|270=100.75|271=300|");
1279 let mut dec = Decoder::new();
1280 let msg = dec.decode(&raw).unwrap();
1281
1282 let entries: Vec<_> = msg.groups(&MD_ENTRIES).collect();
1283 assert_eq!(entries.len(), 2);
1284
1285 let bid = &entries[0];
1286 assert_eq!(bid.find(tag::MD_ENTRY_TYPE).unwrap().value, b"0");
1287 assert_eq!(bid.find(tag::MD_ENTRY_PX).unwrap().value, b"100.50");
1288 assert_eq!(bid.find(tag::MD_ENTRY_SIZE).unwrap().value, b"500");
1289
1290 let offer = &entries[1];
1291 assert_eq!(offer.find(tag::MD_ENTRY_TYPE).unwrap().value, b"1");
1292 assert_eq!(offer.find(tag::MD_ENTRY_PX).unwrap().value, b"100.75");
1293 assert_eq!(offer.find(tag::MD_ENTRY_SIZE).unwrap().value, b"300");
1294 }
1295
1296 #[test]
1301 fn multiple_group_types_in_message() {
1302 let raw =
1304 fix("35=D|136=1|137=1.50|138=USD|139=4|215=2|216=1|217=ROUTE_A|216=2|217=ROUTE_B|");
1305 let mut dec = Decoder::new();
1306 let msg = dec.decode(&raw).unwrap();
1307
1308 let fees: Vec<_> = msg.groups(&MISC_FEES).collect();
1310 assert_eq!(fees.len(), 1);
1311 assert_eq!(fees[0].find(tag::MISC_FEE_AMT).unwrap().value, b"1.50");
1312
1313 let routes: Vec<_> = msg.groups(&ROUTING_IDS).collect();
1315 assert_eq!(routes.len(), 2);
1316 assert_eq!(routes[0].find(tag::ROUTING_TYPE).unwrap().value, b"1");
1317 assert_eq!(routes[0].find(tag::ROUTING_ID).unwrap().value, b"ROUTE_A");
1318 assert_eq!(routes[1].find(tag::ROUTING_TYPE).unwrap().value, b"2");
1319 assert_eq!(routes[1].find(tag::ROUTING_ID).unwrap().value, b"ROUTE_B");
1320 }
1321
1322 #[test]
1327 fn group_is_empty_false_for_non_empty() {
1328 let raw = fix("136=1|137=1.0|");
1329 let mut dec = Decoder::new();
1330 let msg = dec.decode(&raw).unwrap();
1331 let g = msg.groups(&MISC_FEES).next().unwrap();
1332 assert!(!g.is_empty());
1333 assert_eq!(g.len(), 1);
1334 }
1335
1336 #[test]
1341 fn nested_group_single_parent_single_child() {
1342 let raw = fix("552=1|54=1|37=ORD1|518=1|519=1|520=100.00|521=USD|");
1344 let mut dec = Decoder::new();
1345 let msg = dec.decode(&raw).unwrap();
1346
1347 let side = msg.groups(&SIDES).next().expect("expected one side");
1348 assert_eq!(side.find(tag::SIDE).unwrap().value, b"1");
1349 assert_eq!(side.find(tag::ORDER_ID).unwrap().value, b"ORD1");
1350
1351 let mut nested = side.groups(&CONT_AMTS);
1352 let ca = nested.next().expect("expected one CONT_AMTS instance");
1353 assert!(nested.next().is_none());
1354 assert_eq!(ca.find(tag::CONT_AMT_TYPE).unwrap().value, b"1");
1355 assert_eq!(ca.find(tag::CONT_AMT_VALUE).unwrap().value, b"100.00");
1356 assert_eq!(ca.find(tag::CONT_AMT_CURR).unwrap().value, b"USD");
1357 }
1358
1359 #[test]
1360 fn nested_group_single_parent_multiple_children() {
1361 let raw = fix("552=1|54=1|518=2|519=1|520=100.00|521=USD|519=2|520=50.00|521=EUR|");
1363 let mut dec = Decoder::new();
1364 let msg = dec.decode(&raw).unwrap();
1365
1366 let side = msg.groups(&SIDES).next().unwrap();
1367 let cont_amts: Vec<_> = side.groups(&CONT_AMTS).collect();
1368 assert_eq!(cont_amts.len(), 2);
1369 assert_eq!(cont_amts[0].find(tag::CONT_AMT_TYPE).unwrap().value, b"1");
1370 assert_eq!(
1371 cont_amts[0].find(tag::CONT_AMT_VALUE).unwrap().value,
1372 b"100.00"
1373 );
1374 assert_eq!(cont_amts[0].find(tag::CONT_AMT_CURR).unwrap().value, b"USD");
1375 assert_eq!(cont_amts[1].find(tag::CONT_AMT_TYPE).unwrap().value, b"2");
1376 assert_eq!(
1377 cont_amts[1].find(tag::CONT_AMT_VALUE).unwrap().value,
1378 b"50.00"
1379 );
1380 assert_eq!(cont_amts[1].find(tag::CONT_AMT_CURR).unwrap().value, b"EUR");
1381 }
1382
1383 #[test]
1384 fn nested_group_multiple_parents_each_with_children() {
1385 let raw = fix("552=2|\
1387 54=1|518=1|519=1|520=100.00|521=USD|\
1388 54=2|518=2|519=1|520=5.00|521=EUR|519=2|520=3.00|521=GBP|");
1389 let mut dec = Decoder::new();
1390 let msg = dec.decode(&raw).unwrap();
1391
1392 let sides: Vec<_> = msg.groups(&SIDES).collect();
1393 assert_eq!(sides.len(), 2);
1394
1395 let side1_cas: Vec<_> = sides[0].groups(&CONT_AMTS).collect();
1396 assert_eq!(side1_cas.len(), 1);
1397 assert_eq!(
1398 side1_cas[0].find(tag::CONT_AMT_VALUE).unwrap().value,
1399 b"100.00"
1400 );
1401 assert_eq!(side1_cas[0].find(tag::CONT_AMT_CURR).unwrap().value, b"USD");
1402
1403 let side2_cas: Vec<_> = sides[1].groups(&CONT_AMTS).collect();
1404 assert_eq!(side2_cas.len(), 2);
1405 assert_eq!(
1406 side2_cas[0].find(tag::CONT_AMT_VALUE).unwrap().value,
1407 b"5.00"
1408 );
1409 assert_eq!(side2_cas[0].find(tag::CONT_AMT_CURR).unwrap().value, b"EUR");
1410 assert_eq!(
1411 side2_cas[1].find(tag::CONT_AMT_VALUE).unwrap().value,
1412 b"3.00"
1413 );
1414 assert_eq!(side2_cas[1].find(tag::CONT_AMT_CURR).unwrap().value, b"GBP");
1415 }
1416
1417 #[test]
1418 fn nested_group_two_different_nested_groups_in_same_parent() {
1419 let raw = fix("552=1|54=1|518=1|519=1|520=100.00|521=USD|136=1|137=10.00|138=EUR|139=1|");
1421 let mut dec = Decoder::new();
1422 let msg = dec.decode(&raw).unwrap();
1423
1424 let side = msg.groups(&SIDES).next().unwrap();
1425
1426 let cont_amts: Vec<_> = side.groups(&CONT_AMTS).collect();
1427 assert_eq!(cont_amts.len(), 1);
1428 assert_eq!(
1429 cont_amts[0].find(tag::CONT_AMT_VALUE).unwrap().value,
1430 b"100.00"
1431 );
1432 assert_eq!(cont_amts[0].find(tag::CONT_AMT_CURR).unwrap().value, b"USD");
1433
1434 let misc_fees: Vec<_> = side.groups(&MISC_FEES).collect();
1435 assert_eq!(misc_fees.len(), 1);
1436 assert_eq!(
1437 misc_fees[0].find(tag::MISC_FEE_AMT).unwrap().value,
1438 b"10.00"
1439 );
1440 assert_eq!(misc_fees[0].find(tag::MISC_FEE_CURR).unwrap().value, b"EUR");
1441 assert_eq!(misc_fees[0].find(tag::MISC_FEE_TYPE).unwrap().value, b"1");
1442 }
1443
1444 #[test]
1445 fn nested_group_count_tag_absent_yields_empty() {
1446 let raw = fix("552=1|54=1|37=ORD1|");
1448 let mut dec = Decoder::new();
1449 let msg = dec.decode(&raw).unwrap();
1450
1451 let side = msg.groups(&SIDES).next().unwrap();
1452 assert!(side.groups(&CONT_AMTS).next().is_none());
1453 }
1454
1455 #[test]
1456 fn nested_group_count_zero_yields_empty() {
1457 let raw = fix("552=1|54=1|37=ORD1|518=0|");
1459 let mut dec = Decoder::new();
1460 let msg = dec.decode(&raw).unwrap();
1461
1462 let side = msg.groups(&SIDES).next().unwrap();
1463 assert!(side.groups(&CONT_AMTS).next().is_none());
1464 }
1465
1466 #[test]
1467 fn nested_group_iter_size_hint() {
1468 let raw = fix("552=1|54=1|518=3|519=1|520=10.00|519=2|520=20.00|519=3|520=30.00|");
1470 let mut dec = Decoder::new();
1471 let msg = dec.decode(&raw).unwrap();
1472
1473 let side = msg.groups(&SIDES).next().unwrap();
1474 let mut nested = side.groups(&CONT_AMTS);
1475 assert_eq!(nested.size_hint(), (3, Some(3)));
1476 nested.next();
1477 assert_eq!(nested.size_hint(), (2, Some(2)));
1478 nested.next();
1479 assert_eq!(nested.size_hint(), (1, Some(1)));
1480 nested.next();
1481 assert_eq!(nested.size_hint(), (0, Some(0)));
1482 assert!(nested.next().is_none());
1483 }
1484}