Functional Weave
Code in TypeScript

net.mac-address@1.0.0

impl/python.py

1,906 bytes · the Python implementation · view raw

from .net_mac_address_types import MacValidation

HEX = "0123456789abcdefABCDEF"


def _invalid(reason: str) -> MacValidation:
    return MacValidation(
        valid=False, canonical=None, reason=reason, multicast=None, locally_administered=None, oui=None
    )


def validate_mac_address(text: str) -> MacValidation:
    """Check a MAC (EUI-48) address written in any of the four common forms and
    normalise it to lower-case colon form.

    Exactly four shapes are accepted: aa:bb:cc:dd:ee:ff, aa-bb-cc-dd-ee-ff,
    aabb.ccdd.eeff (Cisco) and aabbccddeeff. Nothing is trimmed and single-digit
    octets are refused, because "a:b:c:d:e:f" is more often a typo than a MAC.
    """
    if not isinstance(text, str):
        return _invalid("not text")
    if len(text) == 0:
        return _invalid("empty")

    seps = [s for s in (":", "-", ".") if s in text]
    if len(seps) > 1:
        return _invalid("mixed separators")

    if not seps:
        parts = [text]
        shape_ok = len(text) == 12
    else:
        parts = text.split(seps[0])
        if seps[0] == ".":
            shape_ok = len(parts) == 3 and all(len(p) == 4 for p in parts)
        else:
            shape_ok = len(parts) == 6 and all(len(p) == 2 for p in parts)
    if not shape_ok:
        return _invalid("not a recognised MAC address format")

    hexdigits = "".join(parts)
    for ch in hexdigits:
        if ch not in HEX:
            return _invalid("not a hex digit")

    lower = hexdigits.lower()
    octets = [lower[i : i + 2] for i in range(0, 12, 2)]
    first = int(octets[0], 16)
    return MacValidation(
        valid=True,
        canonical=":".join(octets),
        reason=None,
        # The I/G bit is the least significant bit of the first octet, the U/L bit the next.
        multicast=(first & 1) == 1,
        locally_administered=(first & 2) == 2,
        oui="-".join(octets[:3]).upper(),
    )