11from abc import ABC
2- from dataclasses import is_dataclass
3- from typing import Any , Dict , List , Type , TypeVar
2+ from dataclasses import MISSING , is_dataclass
3+ from typing import Any , Dict , List , Type , TypeVar , Union
44from weakref import ReferenceType
55
6-
7- from .types import DataclassType , indexable
6+ from .types import DataclassType , indexable , _Optional
87from .result import Result
98from .utils import get_field_value , unsafe
9+ from .exacting import Regex
1010
1111T = TypeVar ("T" )
1212
@@ -256,7 +256,18 @@ def validate(self, value: Any, **options) -> Result:
256256 f"During validation of dataclass { self !r} at field { name !r} , got:"
257257 )
258258
259- data [name ] = field_res .unwrap ()
259+ field_value = field_res .unwrap ()
260+
261+ ef = field .metadata .get ("exact" )
262+ if ef :
263+ validator_items : List [Validator ] = ef .validators
264+ for item in validator_items :
265+ fv_res = item .validate (field_value )
266+ if not fv_res .is_ok ():
267+ return fv_res
268+ field_value = fv_res .unwrap ()
269+
270+ data [name ] = field_value
260271
261272 if options .get ("from_dict" ):
262273 with unsafe ():
@@ -271,3 +282,66 @@ def __repr__(self):
271282 if dc is None :
272283 raise RuntimeError ("Weakref is gone" )
273284 return dc .__name__
285+
286+
287+ class RegexV (Validator ):
288+ regex : Regex
289+ pattern : str
290+
291+ def __init__ (self , pattern : str ):
292+ self .regex = Regex (pattern )
293+ self .pattern = pattern
294+
295+ def validate (self , value : Any , ** options ) -> "Result" :
296+ res = expect (str , value )
297+ if not res .is_ok ():
298+ return res
299+
300+ data = res .unwrap ()
301+ if not self .regex .validate (data ):
302+ return Result .Err (f"Regex validation { self .pattern !r} on str failed" )
303+
304+ return Result .Ok (data )
305+
306+
307+ class MinMaxV (Validator ):
308+ minv : _Optional [Union [int , float ]]
309+ maxv : _Optional [Union [int , float ]]
310+
311+ def __init__ (
312+ self ,
313+ minv : _Optional [Union [int , float ]] = MISSING ,
314+ maxv : _Optional [Union [int , float ]] = MISSING ,
315+ ):
316+ self .minv = minv
317+ self .maxv = maxv
318+
319+ def validate (self , value : Any , ** options ) -> "Result" :
320+ if hasattr (value , "__len__" ):
321+ ln = len (value )
322+ if self .minv is not MISSING :
323+ if ln < self .minv :
324+ return Result .Err (f"Expected min length of { self .minv } , got { ln } " )
325+ if self .maxv is not MISSING :
326+ if ln > self .maxv :
327+ return Result .Err (f"Expected max length of { self .minv } , got { ln } " )
328+
329+ elif hasattr (value , "__lt__" ) and hasattr (value , "__gt__" ):
330+ if self .minv is not MISSING :
331+ if value < self .minv :
332+ return Result .Err (
333+ f"Expected min value of { self .minv } , got { value !r} "
334+ )
335+
336+ if self .maxv is not MISSING :
337+ if value > self .maxv :
338+ return Result .Err (
339+ f"Expected max value of { self .minv } , got { value !r} "
340+ )
341+
342+ else :
343+ return Result .Err (
344+ f"Neither len(), >, or < can be tested for type { type (value )} "
345+ )
346+
347+ return Result .Ok (value )
0 commit comments