1"""Variable class for URITemplate."""
2
3from __future__ import annotations
4
5from .charset import Charset
6
7
8class VariableInvalidError(Exception):
9 """Exception thrown for invalid variables."""
10
11 variable: str
12
13 def __init__(self, variable: str) -> None:
14 self.variable = variable
15
16 def __str__(self) -> str:
17 """Convert to string."""
18 return 'Bad variable: ' + self.variable
19
20
21class Variable:
22 """
23 A template variable.
24
25 https://tools.ietf.org/html/rfc6570#section-2.3
26 """
27
28 name: str
29 key: str
30 max_length: int
31 explode: bool
32 array: bool
33 default: (str | None)
34
35 def __init__(self, var_spec: str) -> None:
36 self.name = ''
37 self.key = ''
38 self.max_length = 0
39 self.explode = False
40 self.array = False
41 self.default = None
42
43 if (var_spec[0:1] not in Charset.VAR_START):
44 raise VariableInvalidError(var_spec)
45
46 if ('=' in var_spec):
47 var_spec, self.default = var_spec.split('=', 1)
48
49 if (':' in var_spec):
50 var_spec, max_length = var_spec.split(':', 1)
51 if ((0 < len(max_length)) and (len(max_length) < 4)):
52 for digit in max_length:
53 if (digit not in Charset.DIGIT):
54 raise VariableInvalidError(var_spec + ':' + max_length)
55 self.max_length = int(max_length)
56 if (not self.max_length):
57 raise VariableInvalidError(var_spec + ':' + max_length)
58 else:
59 raise VariableInvalidError(var_spec + ':' + max_length)
60 elif ('*' == var_spec[-1]):
61 var_spec = var_spec[:-1]
62 self.explode = True
63 elif ('[]' == var_spec[-2:]):
64 var_spec = var_spec[:-2]
65 self.array = True
66 self.explode = True
67
68 index = 0
69 while (index < len(var_spec)):
70 codepoint = var_spec[index]
71 if (('%' == codepoint)
72 and ((index + 2) < len(var_spec))
73 and (var_spec[index + 1] in Charset.HEX_DIGIT)
74 and (var_spec[index + 2] in Charset.HEX_DIGIT)):
75 self.key += var_spec[index:index + 3]
76 index += 2
77 elif (codepoint in Charset.VAR_CHAR):
78 self.key += codepoint
79 elif ('/' == codepoint):
80 self.name = self.key
81 self.key = ''
82 else:
83 raise VariableInvalidError(var_spec + ((':' + str(self.max_length)) if (self.max_length) else '')
84 + ('[]' if (self.array) else ('*' if (self.explode) else '')))
85 index += 1
86
87 self.name = (self.name or self.key)
88 self.key = (self.key or self.name)
89
90 def __str__(self) -> str:
91 """Convert to string."""
92 return (self.name + (f'/{self.key}' if (self.key and (self.key != self.name)) else '')
93 + (f':{self.max_length}' if (self.max_length) else '')
94 + ('*' if (self.explode and not self.array) else '') + ('[]' if (self.array) else '')
95 + (f'={self.default}' if (self.default is not None) else ''))