|  | 
| 1 | 1 | from __future__ import annotations | 
| 2 | 2 | 
 | 
| 3 |  | -import re | 
| 4 |  | -from typing import Any | 
| 5 |  | - | 
| 6 |  | -from reactpy_router.types import ConverterMapping, Route | 
|  | 3 | +from reactpy_router.resolvers import StarletteResolver | 
|  | 4 | +from reactpy_router.types import ConversionInfo, Route | 
| 7 | 5 | 
 | 
| 8 | 6 | from reactpy_django.router.converters import CONVERTERS | 
| 9 | 7 | 
 | 
| 10 |  | -PARAM_PATTERN = re.compile(r"<(?P<type>\w+:)?(?P<name>\w+)>") | 
| 11 |  | - | 
| 12 | 8 | 
 | 
| 13 |  | -# TODO: Make reactpy_router's SimpleResolver generic enough to where we don't have to define our own | 
| 14 |  | -class DjangoResolver: | 
|  | 9 | +class DjangoResolver(StarletteResolver): | 
| 15 | 10 |     """A simple route resolver that uses regex to match paths""" | 
| 16 | 11 | 
 | 
| 17 |  | -    def __init__(self, route: Route) -> None: | 
| 18 |  | -        self.element = route.element | 
| 19 |  | -        self.pattern, self.converters = parse_path(route.path) | 
| 20 |  | -        self.key = self.pattern.pattern | 
| 21 |  | - | 
| 22 |  | -    def resolve(self, path: str) -> tuple[Any, dict[str, Any]] | None: | 
| 23 |  | -        match = self.pattern.match(path) | 
| 24 |  | -        if match: | 
| 25 |  | -            return ( | 
| 26 |  | -                self.element, | 
| 27 |  | -                {k: self.converters[k](v) for k, v in match.groupdict().items()}, | 
| 28 |  | -            ) | 
| 29 |  | -        return None | 
| 30 |  | - | 
| 31 |  | - | 
| 32 |  | -# TODO: Make reactpy_router's parse_path generic enough to where we don't have to define our own | 
| 33 |  | -def parse_path(path: str) -> tuple[re.Pattern[str], ConverterMapping]: | 
| 34 |  | -    # Convert path to regex pattern, and make sure to interpret the registered converters (ex. <int:foo>) | 
| 35 |  | -    pattern = "^" | 
| 36 |  | -    last_match_end = 0 | 
| 37 |  | -    converters: ConverterMapping = {} | 
| 38 |  | -    for match in PARAM_PATTERN.finditer(path): | 
| 39 |  | -        param_name = match.group("name") | 
| 40 |  | -        param_type = (match.group("type") or "str").strip(":") | 
| 41 |  | -        try: | 
| 42 |  | -            param_conv = CONVERTERS[param_type] | 
| 43 |  | -        except KeyError as e: | 
| 44 |  | -            raise ValueError( | 
| 45 |  | -                f"Unknown conversion type {param_type!r} in {path!r}" | 
| 46 |  | -            ) from e | 
| 47 |  | -        pattern += re.escape(path[last_match_end : match.start()]) | 
| 48 |  | -        pattern += f"(?P<{param_name}>{param_conv['regex']})" | 
| 49 |  | -        converters[param_name] = param_conv["func"] | 
| 50 |  | -        last_match_end = match.end() | 
| 51 |  | -    pattern += f"{re.escape(path[last_match_end:])}$" | 
| 52 |  | - | 
| 53 |  | -    # Replace literal `*` with "match anything" regex pattern, if it's at the end of the path | 
| 54 |  | -    if pattern.endswith(r"\*$"): | 
| 55 |  | -        pattern = f"{pattern[:-3]}.*$" | 
| 56 |  | - | 
| 57 |  | -    return re.compile(pattern), converters | 
|  | 12 | +    def __init__( | 
|  | 13 | +        self, | 
|  | 14 | +        route: Route, | 
|  | 15 | +        param_pattern=r"<(?P<type>\w+:)?(?P<name>\w+)>", | 
|  | 16 | +        converters: dict[str, ConversionInfo] | None = None, | 
|  | 17 | +    ) -> None: | 
|  | 18 | +        super().__init__( | 
|  | 19 | +            route=route, | 
|  | 20 | +            param_pattern=param_pattern, | 
|  | 21 | +            converters=converters or CONVERTERS, | 
|  | 22 | +        ) | 
0 commit comments