fixlite 0.3.27

fixlite FIX parser core library
Documentation

fixlite

fixlite is a Rust crate for parsing and building FIX (Financial Information eXchange) protocol messages. It provides a procedural macro, FixDeserialize, to automatically generate deserialization implementations for your structs, a macro, fix_tag_registry!, to define registries that map FIX tags to their corresponding Rust types, and a FixBuilder that encodes values via the FixValue trait.

Features

  • Automatic deserialization of FIX messages into Rust structs using #[derive(FixDeserialize)].
  • Support for FIX components and repeating groups via attributes.
  • Customizable tag-to-type mappings through registries defined with fix_tag_registry!.
  • Compile-time validation of tag-type associations to ensure correctness.
  • Zero-copy deserialization for string fields defined as &str, enhancing performance by avoiding unnecessary allocations.
  • Message building with FixBuilder and the build_fix! macro.
  • Trait-based field encoding via FixValue and AsFixStr for enums.
  • Optional BodyLength/CheckSum validation during parsing when the checksum feature is enabled.

Usage

Defining a Registry

Use the fix_tag_registry! macro to define a registry that maps FIX tags to their corresponding Rust types. This registry is used during deserialization to validate and parse tag values correctly.

use fixlite::fix_tag_registry;

fix_tag_registry! {
    MyRegistry {
        35 => [fixlite::fix::MsgType],
        31 => [f64], // LastPx
        8001 => [f64],
    }
}

You can also define an empty registry:

fix_tag_registry!(EmptyRegistry);

Deserializing FIX Messages

Annotate your struct with #[derive(FixDeserialize)] and use the provided attributes to specify how each field corresponds to FIX tags. To validate BodyLength and CheckSum during parsing, enable the checksum feature:

fixlite = { version = "...", features = ["checksum"] }
use fixlite::FixDeserialize;

#[derive(FixDeserialize, Debug)]
#[fix_registry(MyRegistry)]
struct TestMessage<'a> {
    #[fix(tag = 35)]
    msg_type: fixlite::fix::MsgType,

    #[fix(tag = 31)]
    last_px: Option<f64>,

    #[fix(component)]
    header: Header<'a>,

    #[fix_group(tag = 453)]
    parties: Vec<Party<'a>>,

    #[fix(tag = 55)]
    symbol: &'a str, // Zero-copy deserialization
}

Building FIX Messages

Use FixBuilder directly or via the build_fix! macro. Types that implement FixValue can be encoded, and FIX enums implement AsFixStr automatically.

use chrono::Utc;
use fixlite::build_fix;
use fixlite::fix::{FixBuilder, HandlInst, MsgType, OrdType, Side, TimeInForce};

let mut builder = FixBuilder::new("FIX.4.2", "BUYER", "SELLER");
let dt = Utc::now();

let msg = build_fix!(
    builder,
    2u64,
    dt,
    MsgType::NewOrderSingle,
    11, "123",
    21, HandlInst::Automated,
    55, "IBM",
    54, Side::Buy,
    38, 100u32,
    40, OrdType::Limit,
    44, "150.25",
    59, TimeInForce::Day,
);

Attributes

  • #[fix(tag = N)]: Maps the field to FIX tag N.
  • #[fix(component)]: Indicates that the field is a nested component.
  • #[fix_group(tag = N)]: Indicates that the field is a repeating group starting with tag N.
  • #[fix_registry(RegistryName)]: Specifies the registry to use for tag-type validation. Defaults to DefaultRegistry if not specified.

Zero-Copy Deserialization

For string fields defined as &str, fixlite supports zero-copy deserialization. This means that during deserialization, the string slices in the FIX message are borrowed directly, avoiding unnecessary allocations and enhancing performance.

Ensure that the lifetime annotations are correctly specified to take advantage of this feature.

Example

Given a FIX message:

8=FIX.4.2|9=176|35=D|49=BUYER|56=SELLER|34=2|52=20190605-19:45:32.123|11=123|21=1|55=IBM|54=1|38=100|40=2|44=150.25|59=0|10=128|

You can deserialize using from_fix() for SOH delimiter. If your input uses another separator, replace it before parsing:

let raw = b"8=FIX.4.2|9=176|35=D|49=BUYER|56=SELLER|34=2|52=20190605-19:45:32.123|11=123|21=1|55=IBM|54=1|38=100|40=2|44=150.25|59=0|10=128|";
let message = raw
    .iter()
    .map(|&b| if b == b'|' { b'\x01' } else { b })
    .collect::<Vec<u8>>();
let parsed = TestMessage::from_fix(&message)?;

License

This project is dual-licensed under MIT OR LGPL-3.0-or-later. See the LICENSE-MIT and LICENSE-LGPL-3.0 files for details.