Skip to main content

Crate edifact_rs_derive

Crate edifact_rs_derive 

Source
Expand description

Derive macros for EdifactSerialize and EdifactDeserialize.

§Segment struct (single segment)

#[derive(EdifactSerialize, EdifactDeserialize)]
#[edifact(segment = "BGM")]
pub struct BgmSegment {
    #[edifact(element = 0)]
    pub doc_name_code: String,
    #[edifact(element = 1)]
    pub doc_id: String,
    #[edifact(element = 2)]
    pub msg_function: Option<String>,
}

§Segment struct with qualifier

#[derive(EdifactSerialize, EdifactDeserialize)]
#[edifact(segment = "NAD", qualifier = "MS")]
pub struct NadMs {
    #[edifact(element = 1)]
    pub party_id: String,
}

§Message struct (multiple segments)

#[derive(EdifactSerialize, EdifactDeserialize)]
pub struct OrdersMessage {
    pub bgm: BgmSegment,
    pub buyer: NadMs,
    #[edifact(group)]
    pub lines: Vec<LinSegment>,
}

§#[edifact(group)] and Vec<T> fields

The #[edifact(group)] attribute marks a Vec<T> field as a contiguous group of repeated segments. Without the attribute, Vec<T> on a segment struct collects all matching segments from the window into the Vec.

Note: #[edifact(group)] enforces two compile-time structural constraints:

  1. The annotated field must be of type Vec<T> — any other type is rejected with a clear error message.
  2. #[edifact(group)] cannot be combined with #[edifact(element = ...)] or #[edifact(component = ...)] — positional placement and group semantics are mutually exclusive.

At runtime the generated deserialization code collects contiguous occurrences of the inner segment type T into the Vec using edifact_rs::contiguous_groups_by_qualifier. This is behaviorally different from a bare Vec<T> without #[edifact(group)], which uses edifact_rs::find_segments_typed and does not enforce contiguity.

§#[edifact(required)] on Option<T> fields

By default, Option<T> fields produce None when the element is absent. Annotating an Option<T> field with #[edifact(required)] changes this: instead of None, deserialization returns edifact_rs::EdifactError::MissingRequiredElement when the element is absent or empty. The Rust type stays Option<T>, which is useful when the EDIFACT specification mandates the element but your domain model treats it as optional for other reasons.

#[derive(EdifactSerialize, EdifactDeserialize)]
#[edifact(segment = "DTM")]
pub struct DtmSegment {
    #[edifact(element = 0)]
    qualifier: String,
    /// Required by the spec but kept as Option in the domain model.
    #[edifact(element = 1, required)]
    date_time: Option<String>,
    #[edifact(element = 2)]
    format_code: Option<String>,
}

§Non-String fields and Display / FromStr

Non-String field types (e.g. u32, bool, your own newtype) are serialized via Display and deserialized via FromStr. The derive macro does not add a compile-time bound; if the type does not implement both traits the generated code will fail to compile with a standard “trait not satisfied” error.

To avoid surprises, ensure any non-String field type implements both:

impl std::fmt::Display for MyCode { ... }
impl std::str::FromStr for MyCode { ... }

Derive Macros§

EdifactDeserialize
Derive edifact_rs::EdifactDeserialize for segment or message structs.
EdifactSerialize
Derive edifact_rs::EdifactSerialize for segment or message structs.