Getting Started

Install edifact-rs, parse your first EDIFACT interchange, and choose the feature flags you need.

On this page
  1. Prerequisites
  2. 1. Add the dependency
    1. Optional features
  3. 2. Parse your first EDIFACT message
  4. 3. Access segment data
  5. 4. Typed mapping with derive macros
  6. 5. Validate a message
  7. 6. Process a reader (large files)
  8. 7. Next steps

This guide walks you from zero to a working edifact-rs integration in under five minutes.


Prerequisites

  • Rust 1.85 or later (edition 2024)
  • A Cargo workspace or binary / library project

Check your toolchain:

rustup show        # active toolchain
rustup update      # upgrade to latest stable

1. Add the dependency

cargo add edifact-rs

The derive feature is enabled by default, which re-exports the EdifactDeserialize / EdifactSerialize derive macros — and their EdifactCompositeDeserialize / EdifactCompositeSerialize counterparts — from edifact-rs-derive.

Optional features

FeatureDefaultWhat it adds
derive✅ yesProc-macro derive for typed structs
diagnostics❌ nomiette::Diagnostic on EdifactError — human-friendly output

Enable diagnostics:

cargo add edifact-rs --features diagnostics

Disable derive macros (core parsing only):

cargo add edifact-rs --no-default-features

2. Parse your first EDIFACT message

use edifact_rs::from_bytes;

fn main() -> Result<(), edifact_rs::EdifactError> {
    // Minimal ORDERS interchange (UNA is optional)
    let input = b"UNA:+.? '\
                  UNH+1+ORDERS:D:11A:UN'\
                  BGM+220+PO-4711+9'\
                  NAD+BY+4000001000002::9'\
                  UNT+4+1'";

    let segments: Vec<_> = from_bytes(input).collect::<Result<_, _>>()?;

    for seg in &segments {
        println!("{} ({} elements)", seg.tag, seg.elements.len());
    }
    // UNH (2 elements)
    // BGM (3 elements)
    // NAD (2 elements)
    // UNT (2 elements)

    Ok(())
}

from_bytes returns an iterator of Result<Segment<'_>, EdifactError>. Each Segment borrows directly from input — zero heap allocation for tag and element text.


3. Access segment data

use edifact_rs::from_bytes;

fn main() -> Result<(), edifact_rs::EdifactError> {
    let input = b"BGM+220+PO-4711+9'";
    let segs: Vec<_> = from_bytes(input).collect::<Result<_, _>>()?;
    let bgm = &segs[0];

    // element_str(n) — shorthand for component 0 of element n
    assert_eq!(bgm.element_str(0), Some("220"));
    assert_eq!(bgm.element_str(1), Some("PO-4711"));

    // get_element(n) then get_component(c) for composite access
    let doc_code = bgm
        .get_element(0)
        .and_then(|e| e.get_component(0))
        .unwrap_or_default();
    assert_eq!(doc_code, "220");

    Ok(())
}

4. Typed mapping with derive macros

Instead of accessing elements by index, declare a struct:

use edifact_rs::{EdifactDeserialize, EdifactSerialize, from_bytes};

#[derive(Debug, EdifactDeserialize, EdifactSerialize)]
#[edifact(segment = "BGM")]
struct Bgm {
    #[edifact(element = 0)]
    document_name_code: String,
    #[edifact(element = 1)]
    document_number: String,
    #[edifact(element = 2)]
    function_code: Option<String>,
}

fn main() -> Result<(), edifact_rs::EdifactError> {
    let input = b"BGM+220+PO-4711+9'";
    let segs: Vec<_> = from_bytes(input).collect::<Result<_, _>>()?;
    let bgm = Bgm::edifact_deserialize(&segs)?;

    println!("doc_code={}", bgm.document_name_code);
    println!("doc_number={}", bgm.document_number);
    Ok(())
}

→ Full derive reference: Typed Derive


5. Validate a message

use edifact_rs::{
    ValidationContext, ValidationLayer, Validator, ValidationReport, ValidationRuleContext,
    Segment, from_bytes,
};

struct MyValidator;

impl Validator for MyValidator {
    fn validate_batch(
        &self,
        _segments: &[Segment<'_>],
        _report: &mut ValidationReport,
        _context: &ValidationRuleContext<'_>,
    ) {
        // your validation logic here
    }
}

fn main() -> Result<(), edifact_rs::EdifactError> {
    let input = b"UNH+1+ORDERS:D:11A:UN'BGM+220+PO-4711+9'UNT+3+1'";
    let segs: Vec<_> = from_bytes(input).collect::<Result<_, _>>()?;

    let ctx = ValidationContext::builder()
        .with_message_type("ORDERS")
        .with_validator(ValidationLayer::Structure, MyValidator)
        .build();

    let report = ctx.validate_lenient(&segs);
    if report.is_valid() {
        println!("✅ valid");
    } else {
        for issue in report.errors() {
            eprintln!("{}", issue.message);
        }
    }
    Ok(())
}

→ Full validation guide: Validation


6. Process a reader (large files)

use edifact_rs::from_reader;
use std::fs::File;

fn main() -> Result<(), edifact_rs::EdifactError> {
    let f = File::open("interchange.edi")?;
    for result in from_reader(f) {
        let segment = result?;
        println!("{}", segment.tag);
    }
    Ok(())
}

from_reader parses one OwnedSegment at a time without buffering the whole file.

→ Full streaming guide: Streaming


7. Next steps

GoalGuide
Understand UNA, delimiters, release charsCore Concepts
Parse byte slices efficientlyParsing
Write EDIFACT outputWriting
Derive typed structs for segments and messagesTyped Derive
Stream multi-message interchangesStreaming
Add business-rule validationProfile Packs
Pretty-print errors in a CLIDiagnostics
Integrate with async / tokioAsync Integration