diff --git a/src/python/jw/pkg/lib/Uri.py b/src/python/jw/pkg/lib/Uri.py new file mode 100644 index 00000000..d9a28fc1 --- /dev/null +++ b/src/python/jw/pkg/lib/Uri.py @@ -0,0 +1,122 @@ +# -*- coding: utf-8 -*- + +from __future__ import annotations + +from typing import TYPE_CHECKING +from functools import cached_property + +if TYPE_CHECKING: + from typing import Self + import urllib + +# Make sure URIs are interpreted indentically everywhere + +class Uri: + + def __assemble(self, scheme: bool, credentials: bool, secure: bool) -> str: + ret = '' + if scheme: + ret += f'{self.protocol}://' + if credentials and self.username: + ret += self.username + if self.password: + ret += ':' + '' if secure else self.password + ret += '@' + if self.hostname: + ret += self.hostname + if self.port_str: + ret += ':' + self.port_str + return ret + + def __init__(self, string: str) -> None: + self.__string = string + self.__username: str|None = None + self.__password: str|None = None + + def __repr__(self) -> str: + return self.full + + def __str__(self) -> str: + return self.id + + @cached_property + def __p(self) -> urllib.parse.ParseResult: + from urllib.parse import urlparse + return urlparse(self.__string) + + @classmethod + def pimp(cls, url: str|Self) -> Uri: + if isinstance(url, Uri): + return url + return Uri(url) + + @property + def to_string(self) -> str: + return self.__string + + @cached_property + def scheme(self) -> str: + ret = self.__p.scheme + if not ret: + return 'file://' + return ret + + @cached_property + def protocol(self) -> str: + return self.scheme.replace('://', '') + + @property + def username(self) -> str|None: + if self.__username is None: + return self.__p.username + return self.__username + + def set_username(self, username: str) -> None: + self.__username = username + + @property + def password(self) -> str|None: + if self.__password is None: + return self.__p.password + return self.__password + + def set_password(self, password: str) -> None: + self.__password = password + + @cached_property + def hostname(self) -> str|None: + return self.__p.hostname + + @cached_property + def port(self) -> int|None: + return self.__p.port + + @cached_property + def port_str(self) -> str|None: + if self.port is None: + return None + return str(self.port) + + @cached_property + def authority(self) -> str: + return self.__assemble(scheme=False, credentials=True, secure=False) + + @cached_property + def origin(self) -> str: + return self.__assemble(scheme=False, credentials=False, secure=True) + + @cached_property + def scheme_plus_authority(self) -> str: + return self.scheme + '://' + self.authority + + @cached_property + def id(self) -> str: + return self.__assemble(scheme=True, credentials=True, secure=True) + + @cached_property + def full(self) -> str: + return self.__assemble(scheme=True, credentials=True, secure=False) + self.path + + @cached_property + def path(self) -> str: + return self.__p.path