From 729485091463b9c1cb56f2a1061a8131db223b21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B4=9B=E6=B0=B4=E5=B1=85=E5=AE=A4?= Date: Mon, 1 May 2023 19:24:44 +0800 Subject: [PATCH] :sparkles: Add Routes --- simnet/client/routes.py | 156 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 simnet/client/routes.py diff --git a/simnet/client/routes.py b/simnet/client/routes.py new file mode 100644 index 0000000..808192e --- /dev/null +++ b/simnet/client/routes.py @@ -0,0 +1,156 @@ +from typing import Dict, Union +from urllib.parse import urljoin + +from httpx import URL as _URL + +from simnet.utils.enum_ import Region, Game + +URLTypes = Union["URL", str] + + +class URL(_URL): + """A subclass of httpx's URL class, with additional convenience methods for URL manipulation.""" + + def join(self, url: URLTypes) -> "URL": + """ + Join the current URL with the given URL. + + Args: + url (Union[URL, str]): The URL to join with. + + Returns: + URL: A new URL instance representing the joined URL. + + """ + return URL(urljoin(str(self), str(URL(url)))) + + def __truediv__(self, url: URLTypes) -> "URL": + """ + Append the given URL to the current URL using the '/' operator. + + Args: + url (Union[URL, str]): The URL to append. + + Returns: + URL: A new URL instance representing the joined URL. + + """ + return URL(urljoin(str(self) + "/", str(URL(url)))) + + +class BaseRoute: + """A base class for defining routes with useful metadata.""" + + +class Route(BaseRoute): + """A standard route with a single URL.""" + + url: URL + + def __init__(self, url: str) -> None: + """ + Initialize a Route instance. + + Args: + url (str): The URL for this route. + + """ + self.url = URL(url) + + def get_url(self) -> URL: + """ + Get the URL for this route. + + Returns: + URL: The URL for this route. + + """ + return self.url + + +class InternationalRoute(BaseRoute): + """A route with URLs for both the overseas and Chinese regions.""" + + urls: Dict[Region, URL] + + def __init__(self, overseas: str, chinese: str) -> None: + """ + Initialize an InternationalRoute instance. + + Args: + overseas (str): The URL for the overseas region. + chinese (str): The URL for the Chinese region. + + """ + self.urls = { + Region.OVERSEAS: URL(overseas), + Region.CHINESE: URL(chinese), + } + + def get_url(self, region: Region) -> URL: + """ + Get the URL for the given region. + + Args: + region (Region): The region to get the URL for. + + Returns: + URL: The URL for the given region. + + Raises: + RuntimeError: If the given region is not supported. + + """ + if not self.urls[region]: + raise RuntimeError(f"URL does not support {region.name} region.") + + return self.urls[region] + + +class GameRoute(BaseRoute): + """A route with URLs for different games and regions.""" + + urls: Dict[Region, Dict[Game, URL]] + + def __init__( + self, + overseas: Dict[str, str], + chinese: Dict[str, str], + ) -> None: + """ + Initialize a GameRoute instance. + + Args: + overseas (Dict[str, str]): A dictionary mapping game names to URLs for the overseas region. + chinese (Dict[str, str]): A dictionary mapping game names to URLs for the Chinese region. + + """ + self.urls = { + Region.OVERSEAS: {Game(game): URL(url) for game, url in overseas.items()}, + Region.CHINESE: {Game(game): URL(url) for game, url in chinese.items()}, + } + + def get_url(self, region: Region, game: Game) -> URL: + """ + Get the URL for the given region and game. + + Args: + region (Region): The region to get the URL for. + game (Game): The game to get the URL for. + + Returns: + URL: The URL for the given region and game. + + Raises: + RuntimeError: If the given region or game is not supported. + + """ + if not self.urls[region]: + raise RuntimeError(f"URL does not support {region.name} region.") + + if not self.urls[region][game]: + raise RuntimeError( + f"URL does not support {game.name} game for {region.name} region." + ) + + return self.urls[region][game]