SBE Feature Tour

Standalone laboratory sample for ergo-sbe (publish = false). This is the crates.io / docs.rs teaching entry.

Conversion: three styles in one crate

build.rs uses different APIs for different selectors:

#![allow(unused)]
fn main() {
    let generated_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src/generated");
    let config = ergo_sbe::GenerationConfig::new("feature_tour")
        .with_domain_objects(ergo_sbe::DomainVarData::Strings)
        .with_domain_type(
            ergo_sbe::ConversionSelector::named_type("BooleanType"),
            "bool")
        .with_domain_type(
            ergo_sbe::ConversionSelector::semantic_type("UTCTimestamp"),
            "chrono::DateTime<chrono::Utc>")
        .with_conversion(ergo_sbe::ConversionSelector::named_type("Decimal"))
        // Same shape as Decimal, but the app supplies the impl itself — see
        // demo_domain_type_manual_impl in src/lib.rs.
        .with_manual_domain_type(
            ergo_sbe::ConversionSelector::named_type("ManualDecimal"),
            "rust_decimal::Decimal");

    ergo_sbe::generate_to_dir("schemas/feature-tour.xml", config, &generated_dir)?;
}

(The real build.rs — this code is compiled and tested in CI.)

The generated code is included via #[path = "generated/feature_tour.rs"] — no sbe_mod! needed. See Build Patterns.

SelectorConfigDecode APIEncode APIWho writes the impl?
BooleanTypewith_domain_type(.., "bool")dec.try_available()?enc.try_available(true)?ergo-sbe
UTCTimestampwith_domain_type(.., chrono)dec.try_timestamp()?enc.try_timestamp(t)?ergo-sbe
Decimal (Quote)with_conversion onlydec.price_as::<T>()?enc.price_from(&t)?app (generic, any T)
ManualDecimal (Quote)with_manual_domain_type(.., rust_decimal)dec.try_manual_price()?enc.try_manual_price(v)?app (one concrete type)

Runnable proof for the Decimal row: demo_conversion_only in src/lib.rs (uses both rust_decimal and a tiny FixedPrice adapter on the same buffer). Runnable proof for the ManualDecimal row: demo_domain_type_manual_impl — same concrete try_manual_price(...)? signature DomainImpl::Generated would give you, but the impl TryFromSbe<ManualDecimal> / TryToSbe<ManualDecimal> above it are a literal copy-paste of the doc comment ergo-sbe put on the generated method (see with_conversion vs with_domain_type).

Quick rule

  • One fixed app type, ergo-sbe writes the impl → with_domain_type(selector, path)
  • One fixed app type, you write the impl (custom rounding/validation, or overriding the three built-ins) → with_manual_domain_type(selector, path)
  • Pluggable / no forced dep → with_conversion
  • Never call more than one of these for the same selector

Other samples:

SampleStyle
L3 Order Bookwith_domain_type only
Exchange Examplewith_conversion only

Feature map → demo

FeatureDemo
Fixed message + compute_length_with_header()demo_fixed_heartbeat
Staged CarEncodedLengthdemo_car_size_and_encode
Consuming decoder stagesdemo_car_decode_stages
Owned DTOdemo_car_domain_dto
AnyMessagedemo_any_message
bulk_add (fixed-stride leaf group)demo_bulk_add
Checked decode / wrap / verifydemo_try_vs_trusted
Display / Debugdemo_display_debug
with_conversion onlydemo_conversion_only
with_manual_domain_typedemo_domain_type_manual_impl
All of the aboverun_all

Run

cargo run  --manifest-path samples/sbe-feature-tour/Cargo.toml
cargo test --manifest-path samples/sbe-feature-tour/Cargo.toml

After build, generated source is under src/generated/feature_tour.rs.