safeshield 1.3.2__py3-none-any.whl → 1.4.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,157 @@
1
+ from .base import Rule
2
+ from typing import Any, Dict, List, Optional, Set, Union, Tuple, Type
3
+
4
+ class BooleanRule(Rule):
5
+ def validate(self, field: str, value: Any, params: List[str]) -> bool:
6
+ if isinstance(value, bool):
7
+ return True
8
+ if isinstance(value, str):
9
+ return value.lower() in ['true', 'false', '1', '0', 'yes', 'no', 'on', 'off']
10
+ if isinstance(value, int):
11
+ return value in [0, 1]
12
+ return False
13
+
14
+ def message(self, field: str, params: List[str]) -> str:
15
+ return f"The :attribute field must be true or false."
16
+
17
+ class AcceptedRule(Rule):
18
+ def validate(self, field: str, value: Any, params: List[str]) -> bool:
19
+ value = self.get_field_value(value, value)
20
+
21
+ if isinstance(value, str):
22
+ return value.lower() in ['yes', 'on', '1', 1, True, 'true', 'True']
23
+ if isinstance(value, int):
24
+ return value == 1
25
+ if isinstance(value, bool):
26
+ return value
27
+ return False
28
+
29
+ def message(self, field: str, params: List[str]) -> str:
30
+ return f"The :attribute must be accepted."
31
+
32
+ class DeclinedRule(Rule):
33
+ def validate(self, field: str, value: Any, params: List[str]) -> bool:
34
+ value = self.get_field_value(value, value)
35
+
36
+ if isinstance(value, str):
37
+ return value.lower() in ['no', 'off', '0', 0, False, 'false', 'False']
38
+ if isinstance(value, int):
39
+ return value == 0
40
+ if isinstance(value, bool):
41
+ return not value
42
+ return False
43
+
44
+ def message(self, field: str, params: List[str]) -> str:
45
+ return f"The :attribute must be declined."
46
+
47
+ class AcceptedIfRule(AcceptedRule):
48
+ def validate(self, field: str, value: Any, params: List[str]) -> bool:
49
+ condition_met = False
50
+
51
+ if len(params) == 1 and callable(params[0]):
52
+ condition_met = params[0](self.validator.data)
53
+ elif len(params) == 1 and isinstance(params[0], bool):
54
+ condition_met = params[0]
55
+ else:
56
+ conditions = list(zip(params[::2], params[1::2]))
57
+
58
+ for other_field, expected_value in conditions:
59
+ if not other_field or expected_value is None:
60
+ continue
61
+
62
+ actual_value = self.get_field_value(other_field, '')
63
+
64
+ if actual_value == expected_value:
65
+ condition_met = True
66
+ break
67
+
68
+ if condition_met:
69
+ return super().validate(field, value, params)
70
+
71
+ return True
72
+
73
+ class AcceptedUnlessRule(AcceptedRule):
74
+ def validate(self, field: str, value: Any, params: List[str]) -> bool:
75
+ if len(params) < 2:
76
+ return False
77
+
78
+ other_field, other_value = params[0], params[1]
79
+ actual_value = self.get_field_value(other_field, '')
80
+
81
+ if actual_value == other_value:
82
+ return True
83
+
84
+ return super().validate(field, value, params)
85
+
86
+ class AcceptedAllIfRule(AcceptedRule):
87
+ def validate(self, field: str, value: Any, params: List[str]) -> bool:
88
+ if len(params) < 2:
89
+ return False
90
+
91
+ conditions = [(f.strip(), v.strip()) for f, v in zip(params[::2], params[1::2])]
92
+
93
+ all_conditions_met = all(
94
+ self.get_field_value(f) == v
95
+ for f, v in conditions
96
+ )
97
+
98
+ if not all_conditions_met:
99
+ return True
100
+
101
+ return super().validate(field, value, params)
102
+
103
+ class DeclinedIfRule(DeclinedRule):
104
+ def validate(self, field: str, value: Any, params: List[str]) -> bool:
105
+ condition_met = False
106
+
107
+ if len(params) == 1 and callable(params[0]):
108
+ condition_met = params[0](self.validator.data)
109
+ elif len(params) == 1 and isinstance(params[0], bool):
110
+ condition_met = params[0]
111
+ else:
112
+ conditions = list(zip(params[::2], params[1::2]))
113
+
114
+ for other_field, expected_value in conditions:
115
+ if not other_field or expected_value is None:
116
+ continue
117
+
118
+ actual_value = self.get_field_value(other_field, '')
119
+
120
+ if actual_value == expected_value:
121
+ condition_met = True
122
+ break
123
+
124
+ if condition_met:
125
+ return super().validate(field, value, params)
126
+
127
+ return True
128
+
129
+ class DeclinedUnlessRule(DeclinedRule):
130
+ def validate(self, field: str, value: Any, params: List[str]) -> bool:
131
+ if len(params) < 2:
132
+ return False
133
+
134
+ other_field, other_value = params[0], params[1]
135
+ actual_value = self.get_field_value(other_field, '')
136
+
137
+ if actual_value == other_value:
138
+ return True
139
+
140
+ return super().validate(field, value, params)
141
+
142
+ class DeclinedAllIfRule(DeclinedRule):
143
+ def validate(self, field: str, value: Any, params: List[str]) -> bool:
144
+ if len(params) < 2:
145
+ return False
146
+
147
+ conditions = [(f.strip(), v.strip()) for f, v in zip(params[::2], params[1::2])]
148
+
149
+ all_conditions_met = all(
150
+ self.get_field_value(f) == v
151
+ for f, v in conditions
152
+ )
153
+
154
+ if not all_conditions_met:
155
+ return True
156
+
157
+ return super().validate(field, value, params)
@@ -1,11 +1,11 @@
1
- from .base import ValidationRule
2
- from typing import Any, Dict, List, Optional, Set, Union, Tuple, Type
1
+ from .base import Rule
2
+ from typing import Any, Dict, List, Optional, Set, Union, Tuple, Type, Callable
3
3
 
4
4
  # =============================================
5
5
  # COMPARISON VALIDATION RULES
6
6
  # =============================================
7
7
 
8
- class MinRule(ValidationRule):
8
+ class MinRule(Rule):
9
9
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
10
10
  if not params:
11
11
  return False
@@ -29,7 +29,7 @@ class MinRule(ValidationRule):
29
29
  def message(self, field: str, params: List[str]) -> str:
30
30
  return f"The :attribute must be at least {params[0]}."
31
31
 
32
- class MaxRule(ValidationRule):
32
+ class MaxRule(Rule):
33
33
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
34
34
  if not params or len(params) < 1:
35
35
  return False
@@ -93,7 +93,7 @@ class MaxRule(ValidationRule):
93
93
  return f"File :attribute must not exceed {params[0]} bytes"
94
94
  return f"The :attribute must not exceed {params[0]}"
95
95
 
96
- class BetweenRule(ValidationRule):
96
+ class BetweenRule(Rule):
97
97
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
98
98
  if len(params) != 2:
99
99
  return False
@@ -139,7 +139,7 @@ class BetweenRule(ValidationRule):
139
139
  return f"File :attribute must be between {params[0]} and {params[1]} bytes"
140
140
  return f"The :attribute must be between {params[0]} and {params[1]}"
141
141
 
142
- class SizeRule(ValidationRule):
142
+ class SizeRule(Rule):
143
143
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
144
144
  if not params or len(params) < 1:
145
145
  return False
@@ -191,50 +191,140 @@ class SizeRule(ValidationRule):
191
191
 
192
192
  return f"The :attribute must be exactly {params[0]}"
193
193
 
194
- class DigitsRule(ValidationRule):
194
+
195
+ class UniqueRule(Rule):
195
196
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
196
- if not params or not isinstance(value, str):
197
+ if not params or not hasattr(self.validator, 'db_manager') or not self.validator.db_manager:
197
198
  return False
198
199
 
200
+ table = params[0]
201
+ column = field if len(params) == 1 else params[1]
202
+
199
203
  try:
200
- digits = int(params[0])
201
- except ValueError:
204
+ ignore_id = None
205
+ if len(params) > 2 and params[2].startswith('ignore:'):
206
+ ignore_field = params[2].split(':')[1]
207
+ ignore_id = self.get_field_value(ignore_field)
208
+ return self.validator.db_manager.is_unique(table, column, value, ignore_id)
209
+ except Exception as e:
210
+ print(f"Database error in UniqueRule: {e}")
202
211
  return False
203
-
204
- return value.isdigit() and len(value) == digits
205
212
 
206
213
  def message(self, field: str, params: List[str]) -> str:
207
- return f"The :attribute must be {params[0]} digits."
214
+ return f"The :attribute has already been taken."
208
215
 
209
- class DigitsBetweenRule(ValidationRule):
216
+ class ExistsRule(Rule):
210
217
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
211
- if len(params) < 2 or not isinstance(value, str):
218
+ if not params or not hasattr(self.validator, 'db_manager') or not self.validator.db_manager:
212
219
  return False
213
220
 
221
+ table = params[0]
222
+ column = field if len(params) == 1 else params[1]
223
+
214
224
  try:
215
- min_digits = int(params[0])
216
- max_digits = int(params[1])
217
- except ValueError:
225
+ return self.validator.db_manager.exists(table, column, value)
226
+ except Exception as e:
227
+ print(f"Database error in ExistsRule: {e}")
218
228
  return False
219
-
220
- return value.isdigit() and min_digits <= len(value) <= max_digits
221
229
 
222
230
  def message(self, field: str, params: List[str]) -> str:
223
- return f"The :attribute must be between {params[0]} and {params[1]} digits."
231
+ return f"The selected :attribute is invalid."
232
+
233
+ class SameRule(Rule):
234
+ def validate(self, field: str, value: Any, params: List[str]) -> bool:
235
+ if not params:
236
+ return False
237
+ return value == self.get_field_value(params[0])
238
+
239
+ def message(self, field: str, params: List[str]) -> str:
240
+ return f"The :attribute and {params[0]} must match."
224
241
 
225
- class MultipleOfRule(ValidationRule):
242
+ class DifferentRule(Rule):
226
243
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
227
244
  if not params:
228
245
  return False
229
-
230
- try:
231
- divisor = float(params[0])
232
- if divisor == 0:
233
- return False
234
- num = float(value)
235
- return num % divisor == 0
236
- except (ValueError, TypeError):
246
+
247
+ return str(value) != self.get_field_value(params[0])
248
+
249
+ def message(self, field: str, params: List[str]) -> str:
250
+ return f"The :attribute and {params[0]} must be different."
251
+
252
+ class InRule(Rule):
253
+ def validate(self, field: str, value: Any, params: List[str]) -> bool:
254
+ allowed_values = self._parse_option_values(field, params)
255
+ return (str(value) in allowed_values or value in allowed_values)
256
+
257
+ def message(self, field: str, params: List[str]) -> str:
258
+ allowed_values = self._parse_option_values(field, params)
259
+ return f"The selected :attribute must be in : {', '.join(map(str, allowed_values))}"
260
+
261
+ class NotInRule(Rule):
262
+ def validate(self, field: str, value: Any, params: List[str]) -> bool:
263
+ not_allowed_values = self._parse_option_values(field, params)
264
+ return str(value) not in not_allowed_values
265
+
266
+ def message(self, field: str, params: List[str]) -> str:
267
+ not_allowed_values = self._parse_option_values(field, params)
268
+ return f"The selected :attribute must be not in : {', '.join(map(str, not_allowed_values))}"
269
+
270
+ class EnumRule(Rule):
271
+ def __init__(self, *params):
272
+ super().__init__()
273
+ self._params: List[str] = list(params)
274
+ self.allowed_values = self._parse_option_values(None, self._params, raise_for_error=False)
275
+
276
+ def exclude(self, *options):
277
+ except_values = self._parse_option_values(None, options, raise_for_error=False)
278
+ self.allowed_values = [value for value in self.allowed_values if value not in except_values]
279
+ return self
280
+
281
+ def only(self, *options):
282
+ only_values = self._parse_option_values(None, options, raise_for_error=False)
283
+ self.allowed_values = [value for value in self.allowed_values if value in only_values]
284
+ return self
285
+
286
+ def when(self,
287
+ condition: bool,
288
+ when_true: Callable[['EnumRule'], 'EnumRule'],
289
+ when_false: Optional[Callable[['EnumRule'], 'EnumRule']] = None
290
+ ) -> 'EnumRule':
291
+
292
+ if condition:
293
+ return when_true(self)
294
+ elif when_false:
295
+ return when_false(self)
296
+ return self
297
+
298
+ def validate(self, field: str, value: Any, params: List[str]) -> bool:
299
+ return (str(value) in self.allowed_values or value in self.allowed_values)
300
+
301
+ def message(self, field: str, params: List[str]) -> str:
302
+ allowed_values = self._parse_option_values(field, self.allowed_values)
303
+ return f"The :attribute must be one of: {', '.join(map(str, allowed_values))}"
304
+
305
+ class ContainsRule(Rule):
306
+ def validate(self, field: str, value: Any, params: List[str]) -> bool:
307
+ if not params:
237
308
  return False
309
+
310
+ for search_value in params:
311
+ if isinstance(value, str) and search_value in value:
312
+ return True
313
+
314
+ if isinstance(value, (int, float)) and str(search_value) in str(value):
315
+ return True
316
+
317
+ if isinstance(value, (list, tuple, set)) and search_value in value:
318
+ return True
319
+
320
+ if isinstance(value, dict) and search_value in value.keys():
321
+ return True
322
+
323
+ return False
238
324
 
239
325
  def message(self, field: str, params: List[str]) -> str:
240
- return f"The :attribute must be a multiple of {params[0]}."
326
+ if len(params) == 1:
327
+ return f"The {field} must contain {params[0]}"
328
+
329
+ joined_params = ", ".join(params[:-1]) + f" or {params[-1]}"
330
+ return f"The {field} must contain at least one of: {joined_params}"
validator/rules/date.py CHANGED
@@ -1,9 +1,10 @@
1
- from .base import ValidationRule
1
+ from .base import Rule
2
2
  from typing import Any, Dict, List, Optional, Set, Union, Tuple, Type
3
3
  from datetime import datetime
4
+ import zoneinfo
4
5
  from dateutil.parser import parse
5
6
 
6
- class DateRule(ValidationRule):
7
+ class DateRule(Rule):
7
8
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
8
9
  if not isinstance(value, str):
9
10
  return False
@@ -17,16 +18,15 @@ class DateRule(ValidationRule):
17
18
  def message(self, field: str, params: List[str]) -> str:
18
19
  return f"The :attribute is not a valid date."
19
20
 
20
- class DateEqualsRule(ValidationRule):
21
+ class DateEqualsRule(Rule):
21
22
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
22
23
  if not params or not isinstance(value, str):
23
24
  return False
24
25
 
25
- value = self.get_field_value(value, value)
26
-
27
26
  try:
28
27
  date1 = parse(value)
29
28
 
29
+ params = list(params)
30
30
  params[0] = self.get_field_value(params[0], params[0])
31
31
 
32
32
  date2 = parse(params[0])
@@ -37,7 +37,7 @@ class DateEqualsRule(ValidationRule):
37
37
  def message(self, field: str, params: List[str]) -> str:
38
38
  return f"The :attribute must be equal to {params[0]}."
39
39
 
40
- class AfterRule(ValidationRule):
40
+ class AfterRule(Rule):
41
41
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
42
42
  if not params or len(params) < 1:
43
43
  return False
@@ -51,7 +51,8 @@ class AfterRule(ValidationRule):
51
51
  else:
52
52
  return False
53
53
 
54
- # Parse comparison date
54
+
55
+ params = list(params)# Parse comparison date
55
56
  params[0] = self.get_field_value(params[0], params[0])
56
57
  compare_date = parse(params[0])
57
58
 
@@ -63,11 +64,10 @@ class AfterRule(ValidationRule):
63
64
  def message(self, field: str, params: List[str]) -> str:
64
65
  return f"The :attribute must be after {params[0]}"
65
66
 
66
- class AfterOrEqualRule(ValidationRule):
67
+ class AfterOrEqualRule(Rule):
67
68
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
68
69
  if not params or len(params) < 1:
69
70
  return False
70
-
71
71
  try:
72
72
  if isinstance(value, str):
73
73
  date_value = parse(value)
@@ -76,18 +76,19 @@ class AfterOrEqualRule(ValidationRule):
76
76
  else:
77
77
  return False
78
78
 
79
+ params = list(params)
79
80
  params[0] = self.get_field_value(params[0], params[0])
80
81
  compare_date = parse(params[0])
81
82
 
82
83
  return date_value >= compare_date
83
84
 
84
- except (ValueError, TypeError):
85
+ except (ValueError, TypeError) as e:
85
86
  return False
86
87
 
87
88
  def message(self, field: str, params: List[str]) -> str:
88
89
  return f"The :attribute must be after or equal to {params[0]}"
89
90
 
90
- class BeforeRule(ValidationRule):
91
+ class BeforeRule(Rule):
91
92
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
92
93
  if not params or len(params) < 1:
93
94
  return False
@@ -102,6 +103,7 @@ class BeforeRule(ValidationRule):
102
103
  else:
103
104
  return False
104
105
 
106
+ params = list(params)
105
107
  params[0] = self.get_field_value(params[0], params[0])
106
108
  compare_date = parse(params[0])
107
109
 
@@ -113,7 +115,7 @@ class BeforeRule(ValidationRule):
113
115
  def message(self, field: str, params: List[str]) -> str:
114
116
  return f"The :attribute must be before {params[0]}"
115
117
 
116
- class BeforeOrEqualRule(ValidationRule):
118
+ class BeforeOrEqualRule(Rule):
117
119
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
118
120
  if not params or len(params) < 1:
119
121
  return False
@@ -128,6 +130,7 @@ class BeforeOrEqualRule(ValidationRule):
128
130
  else:
129
131
  return False
130
132
 
133
+ params = list(params)
131
134
  params[0] = self.get_field_value(params[0], params[0])
132
135
  compare_date = parse(params[0])
133
136
 
@@ -139,7 +142,7 @@ class BeforeOrEqualRule(ValidationRule):
139
142
  def message(self, field: str, params: List[str]) -> str:
140
143
  return f"The :attribute must be before or equal to {params[0]}"
141
144
 
142
- class DateFormatRule(ValidationRule):
145
+ class DateFormatRule(Rule):
143
146
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
144
147
  if not params or len(params) < 1 or not isinstance(value, str):
145
148
  return False
@@ -151,4 +154,17 @@ class DateFormatRule(ValidationRule):
151
154
  return False
152
155
 
153
156
  def message(self, field: str, params: List[str]) -> str:
154
- return f"The :attribute must match the format {params[0]}"
157
+ return f"The :attribute must match the format {params[0]}"
158
+
159
+ class TimezoneRule(Rule):
160
+ def validate(self, field: str, value: Any, params: List[str]) -> bool:
161
+ if not isinstance(value, str):
162
+ return False
163
+ try:
164
+ zoneinfo.ZoneInfo(value)
165
+ return True
166
+ except zoneinfo.ZoneInfoNotFoundError:
167
+ return False
168
+
169
+ def message(self, field: str, params: List[str]) -> str:
170
+ return f"The :attribute must be a valid timezone."