#!/usr/bin/env python3 # -*- coding: utf-8 -*- from typing import Self, Any, Sequence, Callable from re import Match as REMatch, Pattern as REPattern, compile as re_compile from Abstracts.RouteAbstract import RouteAbstract from Utils.Common import Common from Utils.Checks import Check from Utils.Patterns import RE from Models.RequestModel import RequestModel class RouteModel(RouteAbstract): def __init__(self:Self, inputs:str|dict[str, Any|None]|Sequence[Any|None]) -> None: request:str|REPattern self.method:str self.request:REPattern self.action:str|None = None self.controller:str|None = None self.path:str|None = None self.callback:Callable[[RequestModel], None]|None = None self.permissions:list[str] = [] self.variables:list[str] = [] self.error:int = 0 self.keys:list[str] = [] self.has_keys:bool = False if Check.is_string(inputs): matches:REMatch = RE.ROUTE.match(inputs.strip()) if matches is not None: permissions:str|None method:str|None raw_keys:str|None raw_keys, method, request, self.action, self.controller, self.path, permissions = matches.groups() self.method = "get" if method is None else method.lower() if permissions is not None and (permissions := permissions.strip()) != "": self.permissions.extend(permissions.split(",")) raw_keys and self.keys.extend(raw_keys.split(",")) elif Check.is_dictionary(inputs): preaction:str|Callable[[RequestModel], None]|None = Common.get_value("action", inputs) self.method = Common.get_value("method", inputs, "get").lower() request = Common.get_value("request", inputs, "/") self.controller = Common.get_value("controller", inputs) self.path = Common.get_value("path", inputs) self.permissions.extend(Common.get_value("permissions", inputs, [])) self.keys.extend(Common.get_value("keys", inputs, [])) if Check.is_function(preaction): self.callback = preaction elif Check.is_key(preaction): self.action = preaction elif Check.is_array(inputs): l:int = len(inputs) preaction:str|Callable[[RequestModel], None]|None i:int = 4 if l < 4: self.error = 1 << 1 return self.keys, self.method, request, preaction = inputs[:4] while l > i: if Check.is_key(inputs[i]): self.controller = inputs[i] elif Check.is_array(inputs[i]): self.permissions.extend(inputs[i]) i += 1 if Check.is_function(preaction): self.callback = preaction elif Check.is_key(preaction): self.action = preaction else: self.error = 1 << 0 if not self.error and not self.path and not self.callback and ( not self.action or not self.controller ): self.error = 1 << 2 if not self.error: if Check.is_string(request): def callback(matches:REMatch) -> str: self.variables.append(matches.group(1)) return r'([^\/]+)' self.request = re_compile(r'^' + RE.ROUTE_KEY.sub(callback, Common.to_regular_expression( request[:-1] if request[-1] == "/" else request )) + (r'\/?' if self.path is None else r'(\/.*)?') + r'$') elif Check.is_regular_expression(request): request = request.pattern else: self.error = 1 << 3 self.has_keys = len(self.keys) > 0 def match(self:Self, keys:list[str], method:str, path:str, request:RequestModel) -> bool: is_in_keys:bool = True if self.has_keys: key:str is_in_keys = False for key in keys: if key in self.keys: is_in_keys = True break if is_in_keys and self.method == method.lower(): matches:REMatch = self.request.match(path) if matches is not None: i:int variable:str for i, variable in enumerate(self.variables): request.variables[variable] = matches.group(i + 1) if self.error: request.set_response({ "ok" : False, "code" : 501, "message" : "invalid_route_configuration", "route_error" : self.error }) return True return False