import re from typing import Iterable, overload class NSID: __lexicon_string_format__ = "nsid" PATTERN = re.compile( r"^[a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(\.[a-zA-Z]([a-zA-Z0-9]{0,62})?)$" ) FRAGMENT_PATTERN = re.compile(r"(\.[a-zA-Z]([a-zA-Z0-9]{0,62})?)") __segments: list[str] __fragment: str | None @overload def __init__(self, nsid: str) -> None: ... @overload def __init__(self, nsid: Iterable[str]) -> None: ... def __init__(self, nsid: str | Iterable[str]) -> None: if not isinstance(nsid, str): nsid = ".".join(nsid) if "#" in nsid: nsid, self.__fragment = nsid.rsplit("#", 1) if not self.FRAGMENT_PATTERN.match(self.__fragment): raise ValueError("Invalid NSID: fragment does not match pattern") else: self.__fragment = None if not self.PATTERN.match(nsid): raise ValueError("Invalid NSID: does not match pattern") self.__segments = nsid.split(".") @property def segments(self) -> list[str]: return self.__segments @property def fragment(self) -> str | None: return self.__fragment @property def domain_authority(self) -> list[str]: return self.__segments[:-1] @property def name(self) -> str: return self.__segments[-1] def __str__(self) -> str: return ".".join(self.__segments) + ( "#" + self.__fragment if self.__fragment is not None else "" ) def __repr__(self) -> str: return f"NSID({str(self)})" class NSIDGlob(NSID): PATTERN = re.compile( r"^([a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(\.([a-zA-Z]([a-zA-Z0-9]{0,62})?|\*))|\*)$" ) @classmethod def any(cls): return cls("*") @classmethod def sub_of(cls, nsid: NSID): return cls(str(nsid) + ".*") def matches(self, nsid: NSID): return str(nsid).startswith(str(self)[:-1])