Functional Weave
Code in Rust

net.port-list@1.0.0

impl/python.py

1,927 bytes · the Python implementation · view raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

import re
from typing import List

from .net_port import is_port  ← from net.port ^1.0.0 · built alongside by fune

NUMBER = re.compile(r"[0-9]+")


def _trim_blank(s: str) -> str:
    """Spaces and tabs only: a newline inside a port list is a mistake, not padding."""
    return s.strip(" \t")


def _is_number(s: str) -> bool:
    """Whole ASCII digits with no leading zero: "80", not "080", "+80" or "٨٠"."""
    return NUMBER.fullmatch(s) is not None and (len(s) == 1 or s[0] != "0")


def _port(token: str) -> int:
    # More than five digits cannot be a port.
    value = 0 if len(token) > 5 else int(token)
    if not is_port(value):
        raise ValueError('port "%s" is out of range 1-65535' % token)
    return value


def parse_port_list(text: str) -> List[int]:
    """Parse a port list such as "22,80,8000-8010" into port numbers: each port
    once, in the order first given. Ranges are inclusive.
    """
    if not isinstance(text, str):
        raise TypeError("text must be a string")
    if _trim_blank(text) == "":
        raise ValueError("port list is empty")
    out: List[int] = []
    seen = set()

    def add(p: int) -> None:
        if p not in seen:
            seen.add(p)
            out.append(p)

    for raw in text.split(","):
        item = _trim_blank(raw)
        if item == "":
            raise ValueError("empty item in port list")
        if "-" in item:
            ends = item.split("-")
            if len(ends) != 2 or not _is_number(ends[0]) or not _is_number(ends[1]):
                raise ValueError('invalid port range "%s"' % item)
            low = _port(ends[0])
            high = _port(ends[1])
            if low > high:
                raise ValueError('port range "%s" runs backwards' % item)
            for p in range(low, high + 1):
                add(p)
        else:
            if not _is_number(item):
                raise ValueError('invalid port "%s"' % item)
            add(_port(item))
    return out