|
"""Variable class for URITemplate.""" |
|
|
|
from __future__ import annotations |
|
|
|
from .charset import Charset |
|
|
|
|
|
class VariableInvalidError(Exception): |
|
"""Exception thrown for invalid variables.""" |
|
|
|
variable: str |
|
|
|
def __init__(self, variable: str) -> None: |
|
self.variable = variable |
|
|
|
def __str__(self) -> str: |
|
"""Convert to string.""" |
|
return 'Bad variable: ' + self.variable |
|
|
|
|
|
class Variable: |
|
""" |
|
A template variable. |
|
|
|
https://tools.ietf.org/html/rfc6570#section-2.3 |
|
""" |
|
|
|
name: str |
|
key: str |
|
max_length: int |
|
explode: bool |
|
array: bool |
|
default: (str | None) |
|
|
|
def __init__(self, var_spec: str) -> None: |
|
self.name = '' |
|
self.key = '' |
|
self.max_length = 0 |
|
self.explode = False |
|
self.array = False |
|
self.default = None |
|
|
|
if (var_spec[0:1] not in Charset.VAR_START): |
|
raise VariableInvalidError(var_spec) |
|
|
|
if ('=' in var_spec): |
|
var_spec, self.default = var_spec.split('=', 1) |
|
|
|
if (':' in var_spec): |
|
var_spec, max_length = var_spec.split(':', 1) |
|
if ((0 < len(max_length)) and (len(max_length) < 4)): |
|
for digit in max_length: |
|
if (digit not in Charset.DIGIT): |
|
raise VariableInvalidError(var_spec + ':' + max_length) |
|
self.max_length = int(max_length) |
|
if (not self.max_length): |
|
raise VariableInvalidError(var_spec + ':' + max_length) |
|
else: |
|
raise VariableInvalidError(var_spec + ':' + max_length) |
|
elif ('*' == var_spec[-1]): |
|
var_spec = var_spec[:-1] |
|
self.explode = True |
|
elif ('[]' == var_spec[-2:]): |
|
var_spec = var_spec[:-2] |
|
self.array = True |
|
self.explode = True |
|
|
|
index = 0 |
|
while (index < len(var_spec)): |
|
codepoint = var_spec[index] |
|
if (('%' == codepoint) |
|
and ((index + 2) < len(var_spec)) |
|
and (var_spec[index + 1] in Charset.HEX_DIGIT) |
|
and (var_spec[index + 2] in Charset.HEX_DIGIT)): |
|
self.key += var_spec[index:index + 3] |
|
index += 2 |
|
elif (codepoint in Charset.VAR_CHAR): |
|
self.key += codepoint |
|
elif ('/' == codepoint): |
|
self.name = self.key |
|
self.key = '' |
|
else: |
|
raise VariableInvalidError(var_spec + ((':' + str(self.max_length)) if (self.max_length) else '') |
|
+ ('[]' if (self.array) else ('*' if (self.explode) else ''))) |
|
index += 1 |
|
|
|
self.name = (self.name or self.key) |
|
self.key = (self.key or self.name) |
|
|
|
def __str__(self) -> str: |
|
"""Convert to string.""" |
|
return (self.name + (f'/{self.key}' if (self.key and (self.key != self.name)) else '') |
|
+ (f':{self.max_length}' if (self.max_length) else '') |
|
+ ('*' if (self.explode and not self.array) else '') + ('[]' if (self.array) else '') |
|
+ (f'={self.default}' if (self.default is not None) else '')) |
|
|