Quellcode für pystrich.gs1

"""User-friendly composition of GS1 Application Identifier payloads.

Pair :class:`GS1Fixed` and :class:`GS1Variable` instances with the matching
:meth:`gs1` classmethod on :class:`pystrich.code128.Code128Data` or
:class:`pystrich.datamatrix.DataMatrixData` to emit a GS1-128 or GS1 Data
Matrix payload without managing FNC1 separators by hand.

The library does not ship an Application Identifier registry: callers tell
us whether each field is fixed- or variable-length by choosing the wrapper
class. FNC1 separators are inserted after variable-length fields that are
not the final element of the payload, and once at the very start of the
message.

.. versionadded:: 0.15
"""

from __future__ import annotations

from pystrich.exceptions import PyStrichInvalidInput


def _validate_application_identifier(application_identifier: str) -> None:
    if not isinstance(application_identifier, str) or not (
        application_identifier.isascii()
        and application_identifier.isdigit()
        and 2 <= len(application_identifier) <= 4
    ):
        raise PyStrichInvalidInput(
            f"GS1 Application Identifier must be 2-4 ASCII digits, got {application_identifier!r}"
        )


def _validate_value(value: str) -> None:
    if not isinstance(value, str) or not value:
        raise PyStrichInvalidInput("GS1 field value must be a non-empty str")
    for ch in value:
        if not 0x20 <= ord(ch) <= 0x7E:
            raise PyStrichInvalidInput(
                f"GS1 field value must be printable ASCII (0x20-0x7E); "
                f"{ch!r} (U+{ord(ch):04X}) is not"
            )


class _GS1Field:
    """Shared base for the typed GS1 field wrappers; not part of the public API."""

    __slots__ = ("application_identifier", "value")

    application_identifier: str
    value: str

    def __init__(self, application_identifier: str, value: str) -> None:
        _validate_application_identifier(application_identifier)
        _validate_value(value)
        self.application_identifier = application_identifier
        self.value = value

    def __eq__(self, other):
        if type(self) is not type(other):
            return NotImplemented
        return (
            self.application_identifier == other.application_identifier
            and self.value == other.value
        )

    def __hash__(self):
        return hash((type(self), self.application_identifier, self.value))

    def __repr__(self):
        return f"{type(self).__name__}({self.application_identifier!r}, {self.value!r})"


[Doku] class GS1Fixed(_GS1Field): """A fixed-length GS1 Application Identifier field. The reader knows the data length from the Application Identifier alone, so no FNC1 separator is needed after the field. Use for Application Identifiers such as ``(01)`` GTIN-14, ``(17)`` expiry date, or ``(11)`` production date. :param application_identifier: The 2-4 digit Application Identifier. :param value: The data string. Must be non-empty printable ASCII. """ __slots__ = ()
[Doku] class GS1Variable(_GS1Field): """A variable-length GS1 Application Identifier field. The reader cannot determine where the data ends from the Application Identifier alone, so an FNC1 separator follows unless this is the last field of the payload. Use for Application Identifiers such as ``(10)`` batch, ``(21)`` serial number, or ``(240)`` additional product identification. :param application_identifier: The 2-4 digit Application Identifier. :param value: The data string. Must be non-empty printable ASCII. """ __slots__ = ()