moonbit-barcoder

    GS1-128 and Code 128 data parser toolkit for MoonBit logistics applications.

    barcode
    gs1
    gs1-128
    code128
    logistics
    parser
    Download zip
    Author
    Version
    0.2.1
    License
    Apache-2.0
    Last updated
    2 months ago
    Downloads
    36

    No dependencies

    #moonbit-barcoder

    A MoonBit toolkit for turning GS1-128 scanner payloads into validated, typed logistics data and Code 128 symbols.

    #Project positioning

    Scanner integrations commonly receive text payloads rather than camera frames or bar/space measurements. This library handles the data contract at that boundary:

    • parse GS1 human-readable (01)...(10)... payloads;
    • parse raw scanner data with the ASCII GS/FNC1 separator;
    • validate identifiers, dates, quantities, measurements and profile requirements;
    • encode GS1-128/Code 128 symbol values and render deterministic ASCII previews;
    • process newline-delimited scanner batches without losing per-record diagnostics.

    The root package has no runtime dependencies outside the MoonBit core libraries used by the optional executable.

    #Core capabilities

    #GS1 parsing

    parse_gs1 auto-detects HRI and raw scanner payloads. parse_gs1_with_options provides explicit HRI/raw mode, maximum payload length, group-separator policy and empty-field policy.

    The catalog includes common trade-item and logistic-unit identifiers, commercial amount/price fields, healthcare and product-traceability fields, production data, asset references, and the 310–369 implied-decimal measurement families.

    #Validation and projections

    Validation returns a stable, non-throwing ValidationReport with severity, field path and message. The library includes GTIN, SSCC, GLN, GRAI and date checks, duplicate-field reporting, profile validation, normalized date/measurement views, and a ShipmentProjection for common warehouse workflows.

    #Code 128 and GS1-128

    The encoder plans numeric pairs in Set C, switches to Set B for mixed data, handles FNC1, calculates the weighted checksum, exposes the complete standard symbol pattern table, and renders quiet-zone-aware module previews.

    #Quick start

    ///|
    test "README quick start" {
    let message = parse_gs1("(01)09501101530003(10)LOT42(17)260930")
    assert_eq(message.first_value("01"), Some("09501101530003"))
    assert_eq(message.first_value("10"), Some("LOT42"))
    assert_true(message.validate().ok)
    assert_eq(message.profile().has_expiration, true)
    }

    #CLI

    The runnable example supports a sample payload by default. A payload can be followed by any combination of:

    moon run cmd/main -- --validate --encode --benchmark moon run cmd/main -- "(01)09501101530003(10)LOT42" --validate --encode moon run cmd/main -- "(01)09501101530003(10)LOT42" --ascii

    The executable prints the normalized HRI payload, validation status, encoded symbol/module counts, an ASCII module preview, or a deterministic operation-count benchmark.

    #Library example

    ///|
    test "README Code 128 example" {
    let encoded = encode_code128("123456").unwrap()
    assert_eq(encoded.symbols, [105, 12, 34, 56, 44, 106])
    assert_eq(encoded.checksum, 44)
    assert_true(verify_code128_symbols(encoded.symbols))
    }

    #Architecture

    . |-- types.mbt public GS1 types and base catalog |-- catalog.mbt commercial, logistics and auxiliary AI metadata |-- application_rules.mbt executable AI length/class/check-digit rules |-- measurement.mbt 310–369 measurement families and conversion helpers |-- parser.mbt HRI/raw state-machine parser |-- parser_options.mbt strict/auto/raw parser policies |-- scanner.mbt line and scanner-stream normalization |-- batch.mbt per-record batch results |-- validation.mbt report construction and date rules |-- check_digits.mbt GS1 identifier algorithms |-- validation_profiles.mbt traceability/shipment profiles |-- normalized.mbt dates, implied decimals and safe views |-- pipeline.mbt warehouse projection helpers |-- code128.mbt encoder, checksum and GS1-128 bridge |-- code128_tables.mbt complete Code 128 module patterns |-- benchmark.mbt reproducible operation-count reports |-- cmd/main runnable CLI example `-- .github/workflows check/test and manual publish workflows

    Public concrete types live in the root package. The implementation is split into small files so catalog data, parser state, validation rules and encoding logic can evolve independently.

    #Benchmark

    The benchmark command uses a fixed payload and reports successes, parsed fields and operation counts. It is intentionally deterministic across machines; wall-clock timing should be measured by the invoking environment.

    moon run cmd/main -- --benchmark

    The repository records the exact command and observed output in docs/benchmarks.md. The checked library surface is also available directly:

    ///|
    test "README benchmark example" {
    let report = benchmark_parse(
    "(01)09501101530003(10)BATCH-7(17)260930",
    iterations=10,
    )
    assert_eq(report.successes, 10)
    assert_eq(report.fields, 30)
    assert_true(report.operations > 0)
    }

    #Testing

    Run the complete local gate:

    moon fmt --check moon check --target all --deny-warn moon test --target all --deny-warn moon coverage report -f summary moon coverage analyze

    The test suite covers HRI/raw parsing, separator and length boundaries, malformed fields, check digits, dates, duplicate AIs, measurement conversions, profile requirements, batch retention, Code 128 set planning, FNC1, checksums, symbol patterns, rendering and benchmark invariants.

    #CI

    GitHub Actions runs the MoonBit community-style matrix on Ubuntu, macOS and Windows. Each job installs the latest stable toolchain from the official installer, runs all compiler targets, runs tests, checks formatting, regenerates public interfaces and verifies a clean generated diff. Native coverage is reported as an additional quality signal.

    The publish workflow is manual and uses the repository secret MOONCAKES_MOONBIT_COMMUNITY_TOKEN; credentials are never committed.

    #Release and compatibility

    The module metadata is in moon.mod, the generated public interface is tracked as pkg.generated.mbti, and release notes are maintained in CHANGELOG.md. Public APIs use explicit result/error types where malformed scanner input is expected.

    #License

    Licensed under the Apache License, Version 2.0. See LICENSE.

    ParseError

    pub suberror ParseError {
    UnknownAI(String)
    Truncated(String, Int, Int)
    InvalidSyntax(String)
    } derive(
    Debug
    )

    ApplicationIdentifier

    pub struct ApplicationIdentifier {
    code : String
    title : String
    fixed_length : Int?
    max_length : Int
    kind : ValueKind
    } derive(Eq,
    Debug
    )

    ApplicationRule

    pub struct ApplicationRule {
    code : String
    class : RuleClass
    min_length : Int
    max_length : Int
    fixed : Bool
    check_digit : Bool
    allow_separator : Bool
    } derive(Eq,
    Debug
    )

    BatchRecord

    pub struct BatchRecord {
    index : Int
    message : GS1Message?
    error : String?
    } derive(Eq,
    Debug
    )

    BatchReport

    pub struct BatchReport {
    total : Int
    succeeded : Int
    failed : Int
    records : Array[BatchRecord]
    } derive(Eq,
    Debug
    )

    BatchReport::errors

    fn BatchReport::errors(self : BatchReport) -> Array[String]

    BatchReport::successes

    fn BatchReport::successes(self : BatchReport) -> Array[GS1Message]

    BenchmarkReport

    pub struct BenchmarkReport {
    input_bytes : Int
    iterations : Int
    successes : Int
    fields : Int
    operations : Int
    elapsed_micros : Int
    } derive(Eq,
    Debug
    )

    BenchmarkReport::success_rate

    fn BenchmarkReport::success_rate(self : BenchmarkReport) -> Int

    BenchmarkReport::summary

    fn BenchmarkReport::summary(self : BenchmarkReport) -> String

    Code128Encoded

    pub struct Code128Encoded {
    data : String
    symbols : Array[Int]
    checksum : Int
    symbol_count : Int
    module_count : Int
    set_switches : Int
    } derive(Eq,
    Debug
    )

    Code128Error

    pub enum Code128Error {
    UnsupportedCharacter(Int)
    InvalidFNC1Placement
    EmptyPayload
    } derive(Eq,
    Debug
    )

    GS1Element

    pub struct GS1Element {
    ai : ApplicationIdentifier
    value : String
    } derive(Eq,
    Debug
    )

    GS1Element::code

    fn GS1Element::code(self : GS1Element) -> String

    GS1Element::is_valid

    fn GS1Element::is_valid(self : GS1Element) -> Bool

    GS1Element::title

    fn GS1Element::title(self : GS1Element) -> String

    GS1Element::to_hri

    fn GS1Element::to_hri(self : GS1Element) -> String

    GS1Message

    pub struct GS1Message {
    elements : Array[GS1Element]
    } derive(Eq,
    Debug
    )

    GS1Message::first_value

    fn GS1Message::first_value(self : GS1Message, code : String) -> String?

    GS1Message::normalized_element

    fn GS1Message::normalized_element(self : GS1Message, code : String) -> NormalizedElement?

    GS1Message::profile

    fn GS1Message::profile(self : GS1Message) -> LogisticsProfile

    GS1Message::summary

    fn GS1Message::summary(self : GS1Message) -> String

    GS1Message::to_hri

    fn GS1Message::to_hri(self : GS1Message) -> String

    GS1Message::validate

    fn GS1Message::validate(self : GS1Message) -> ValidationReport

    GS1Message::values

    fn GS1Message::values(self : GS1Message, code : String) -> Array[String]

    LogisticsProfile

    pub struct LogisticsProfile {
    has_sscc : Bool
    has_gtin : Bool
    has_lot : Bool
    has_serial : Bool
    has_expiration : Bool
    } derive(Eq,
    Debug
    )

    MeasurementFamily

    pub struct MeasurementFamily {
    prefix : String
    title : String
    unit : String
    decimal_digits : Bool
    } derive(Eq,
    Debug
    )

    NormalizedElement

    pub struct NormalizedElement {
    code : String
    raw_value : String
    numeric_value : Int?
    decimal_places : Int?
    date_value : String?
    } derive(Eq,
    Debug
    )

    NormalizedElement::code

    fn NormalizedElement::code(self : NormalizedElement) -> String

    NormalizedElement::raw

    fn NormalizedElement::raw(self : NormalizedElement) -> String

    ParseMode

    pub(all) enum ParseMode {
    Auto
    HRI
    Raw
    } derive(Eq,
    Debug
    )

    ParseOptions

    pub struct ParseOptions {
    mode : ParseMode
    max_length : Int
    allow_group_separator : Bool
    allow_empty_variable : Bool
    } derive(Eq,
    Debug
    )

    ParseOptions::auto

    fn ParseOptions::auto(max_length? : Int, allow_group_separator? : Bool) -> ParseOptions

    ParseOptions::group_separator_allowed

    fn ParseOptions::group_separator_allowed(self : ParseOptions) -> Bool

    ParseOptions::max_length

    fn ParseOptions::max_length(self : ParseOptions) -> Int

    ParseOptions::mode

    fn ParseOptions::mode(self : ParseOptions) -> ParseMode

    ParseOptions::raw

    fn ParseOptions::raw(max_length? : Int, allow_group_separator? : Bool) -> ParseOptions

    ParseOptions::strict

    fn ParseOptions::strict(max_length? : Int, allow_group_separator? : Bool) -> ParseOptions

    ParseOptions::with_empty_variable

    fn ParseOptions::with_empty_variable(self : ParseOptions) -> ParseOptions

    ParseOptions::with_max_length

    fn ParseOptions::with_max_length(self : ParseOptions, max_length : Int) -> ParseOptions

    Profile

    pub(all) enum Profile {
    Traceability
    Shipment
    TradeItem
    LogisticUnit
    } derive(Eq,
    Debug
    )

    ProfileReport

    pub struct ProfileReport {
    ok : Bool
    missing : Array[String]
    issues : Array[String]
    } derive(Eq,
    Debug
    )

    RuleClass

    pub(all) enum RuleClass {
    Numeric
    Alphanumeric
    Date
    Decimal
    Reference
    } derive(Eq,
    Debug
    )

    Severity

    pub enum Severity {
    Error
    Warning
    } derive(Eq,
    Debug
    )

    ShipmentProjection

    pub struct ShipmentProjection {
    gtin : String?
    lot : String?
    serial : String?
    expiration : String?
    sscc : String?
    quantity : String?
    } derive(Eq,
    Debug
    )

    ShipmentProjection::identity

    fn ShipmentProjection::identity(self : ShipmentProjection) -> String

    ShipmentProjection::is_traceable

    fn ShipmentProjection::is_traceable(self : ShipmentProjection) -> Bool

    ValidationIssue

    pub struct ValidationIssue {
    severity : Severity
    path : String
    message : String
    } derive(Eq,
    Debug
    )

    ValidationReport

    pub struct ValidationReport {
    ok : Bool
    issues : Array[ValidationIssue]
    } derive(Eq,
    Debug
    )

    ValidationReport::summary

    fn ValidationReport::summary(self : ValidationReport) -> String

    ValueKind

    pub enum ValueKind {
    Fixed
    Variable
    } derive(Eq,
    Debug
    )

    ai_codes_with_prefix

    fn ai_codes_with_prefix(prefix : String) -> Array[String]

    application_identifier_count

    fn application_identifier_count() -> Int

    application_identifiers

    fn application_identifiers() -> Array[ApplicationIdentifier]

    application_rule

    fn application_rule(code : String) -> ApplicationRule?

    application_rule_allows_separator

    fn application_rule_allows_separator(code : String) -> Bool?

    application_rule_class

    fn application_rule_class(code : String) -> String?

    application_rule_codes

    fn application_rule_codes() -> Array[String]

    application_rule_count

    fn application_rule_count() -> Int

    application_rule_is_date

    fn application_rule_is_date(code : String) -> Bool

    application_rule_is_fixed

    fn application_rule_is_fixed(code : String) -> Bool?

    application_rule_is_numeric

    fn application_rule_is_numeric(code : String) -> Bool

    application_rule_is_variable

    fn application_rule_is_variable(code : String) -> Bool?

    application_rule_max_length

    fn application_rule_max_length(code : String) -> Int?

    application_rule_min_length

    fn application_rule_min_length(code : String) -> Int?

    application_rule_needs_check_digit

    fn application_rule_needs_check_digit(code : String) -> Bool?

    application_rule_validate_length

    fn application_rule_validate_length(code : String, value : String) -> Bool

    available_profiles

    fn available_profiles() -> Array[Profile]

    benchmark_parse

    fn benchmark_parse(input : String, iterations~ : Int) -> BenchmarkReport

    catalog_codes

    fn catalog_codes() -> Array[String]

    catalog_has

    fn catalog_has(code : String) -> Bool

    catalog_is_fixed

    fn catalog_is_fixed(code : String) -> Bool?

    catalog_max_length

    fn catalog_max_length(code : String) -> Int?

    catalog_required_length

    fn catalog_required_length(code : String) -> Int?

    catalog_title

    fn catalog_title(code : String) -> String?

    catalog_validate_length

    fn catalog_validate_length(code : String, value : String) -> Bool

    catalog_variable_codes

    fn catalog_variable_codes() -> Array[String]

    check_digit

    fn check_digit(value : String) -> Int?

    code128_checksum

    fn code128_checksum(symbols : ArrayView[Int]) -> Int

    code128_is_bar

    fn code128_is_bar(symbol : Int, unit : Int) -> Bool

    code128_module_count

    fn code128_module_count(symbols : ArrayView[Int]) -> Int

    code128_pattern

    fn code128_pattern(symbol : Int) -> String

    code128_symbol_patterns

    fn code128_symbol_patterns() -> Array[String]

    code128_width

    fn code128_width(symbol : Int) -> Int

    encode_code128

    fn encode_code128(data : String) -> Result[Code128Encoded, Code128Error]

    encode_gs1_128

    fn encode_gs1_128(message : GS1Message) -> Result[Code128Encoded, Code128Error]

    is_gs1_128_symbols

    fn is_gs1_128_symbols(symbols : ArrayView[Int]) -> Bool

    is_valid_code128_symbol

    fn is_valid_code128_symbol(symbol : Int) -> Bool

    lookup_ai

    fn lookup_ai(code : String) -> ApplicationIdentifier?

    measurement_code_prefix

    fn measurement_code_prefix(code : String) -> String?

    measurement_codes_for_prefix

    fn measurement_codes_for_prefix(prefix : String) -> Array[String]

    measurement_compare

    fn measurement_compare(code : String, left : String, right : String) -> Int

    measurement_decimal_places

    fn measurement_decimal_places(code : String) -> Int?

    measurement_description

    fn measurement_description(code : String) -> String?

    measurement_family_codes

    fn measurement_family_codes(prefix : String) -> Array[String]

    measurement_family_count

    fn measurement_family_count() -> Int

    measurement_family_exists

    fn measurement_family_exists(prefix : String) -> Bool

    measurement_family_has_decimal

    fn measurement_family_has_decimal(prefix : String) -> Bool

    measurement_family_title

    fn measurement_family_title(prefix : String) -> String?

    measurement_family_unit

    fn measurement_family_unit(prefix : String) -> String?

    measurement_fixed_width

    fn measurement_fixed_width(code : String) -> Bool

    measurement_from_scaled_integer

    fn measurement_from_scaled_integer(code : String, value : Int) -> String?

    measurement_is_imperial

    fn measurement_is_imperial(code : String) -> Bool

    measurement_is_maximum

    fn measurement_is_maximum(code : String, encoded : String) -> Bool

    measurement_is_metric

    fn measurement_is_metric(code : String) -> Bool

    measurement_is_supported

    fn measurement_is_supported(code : String) -> Bool

    measurement_is_zero

    fn measurement_is_zero(code : String, encoded : String) -> Bool

    measurement_normalize_encoded

    fn measurement_normalize_encoded(code : String, encoded : String) -> String?

    measurement_percent

    fn measurement_percent(code : String, encoded : String) -> Int?

    measurement_range_valid

    fn measurement_range_valid(code : String, encoded : String) -> Bool

    measurement_report

    fn measurement_report(code : String, encoded : String) -> String

    measurement_scale_factor

    fn measurement_scale_factor(code : String) -> Int

    measurement_title

    fn measurement_title(code : String) -> String?

    measurement_to_scaled_integer

    fn measurement_to_scaled_integer(code : String, encoded : String) -> Int?

    measurement_unit

    fn measurement_unit(code : String) -> String?

    measurement_unit_symbol

    fn measurement_unit_symbol(code : String) -> String?

    measurement_uses_implied_decimal

    fn measurement_uses_implied_decimal(code : String) -> Bool

    measurement_value

    fn measurement_value(code : String, encoded : String) -> Int?

    measurement_value_as_decimal

    fn measurement_value_as_decimal(code : String, encoded : String) -> String?

    normalize_scanner_input

    fn normalize_scanner_input(input : String) -> String

    parse_batch

    fn parse_batch(input : String) -> BatchReport

    parse_gs1

    fn parse_gs1(input : String) -> GS1Message raise ParseError

    parse_gs1_with_options

    fn parse_gs1_with_options(input : String, options : ParseOptions) -> GS1Message raise ParseError

    project_shipment

    fn project_shipment(message : GS1Message) -> ShipmentProjection

    render_code128_ascii

    fn render_code128_ascii(encoded : Code128Encoded, quiet_zone? : Int, bar? : String, space? : String) -> String

    scanner_lines

    fn scanner_lines(input : String) -> Array[String]

    scanner_payloads

    fn scanner_payloads(input : String) -> Array[String]

    validate_application_rule

    fn validate_application_rule(code : String, value : String) -> Bool

    validate_giai

    fn validate_giai(value : String) -> Bool

    validate_gln

    fn validate_gln(value : String) -> Bool

    validate_grai

    fn validate_grai(value : String) -> Bool

    validate_gtin

    fn validate_gtin(value : String) -> Bool

    validate_profile

    fn validate_profile(message : GS1Message, profile : Profile) -> ProfileReport

    validate_sscc

    fn validate_sscc(value : String) -> Bool

    verify_code128_symbols

    fn verify_code128_symbols(symbols_with_check : ArrayView[Int]) -> Bool