11use std:: fmt;
22
33use pyo3:: exceptions:: PyValueError ;
4- use pyo3:: types:: { PyDict , PyType } ;
4+ use pyo3:: types:: { PyDict , PyList , PyType } ;
55use pyo3:: { intern, prelude:: * } ;
66
77use crate :: errors:: { ErrorTypeDefaults , InputValue , ValError , ValResult } ;
@@ -42,6 +42,8 @@ impl TryFrom<&str> for InputType {
4242 }
4343}
4444
45+ pub type ValMatch < T > = ValResult < ValidationMatch < T > > ;
46+
4547/// all types have three methods: `validate_*`, `strict_*`, `lax_*`
4648/// the convention is to either implement:
4749/// * `strict_*` & `lax_*` if they have different behavior
@@ -87,13 +89,13 @@ pub trait Input<'py>: fmt::Debug + ToPyObject {
8789
8890 fn validate_dataclass_args < ' a > ( & ' a self , dataclass_name : & str ) -> ValResult < GenericArguments < ' a , ' py > > ;
8991
90- fn validate_str ( & self , strict : bool , coerce_numbers_to_str : bool ) -> ValResult < ValidationMatch < EitherString < ' _ > > > ;
92+ fn validate_str ( & self , strict : bool , coerce_numbers_to_str : bool ) -> ValMatch < EitherString < ' _ > > ;
9193
92- fn validate_bytes < ' a > ( & ' a self , strict : bool ) -> ValResult < ValidationMatch < EitherBytes < ' a , ' py > > > ;
94+ fn validate_bytes < ' a > ( & ' a self , strict : bool ) -> ValMatch < EitherBytes < ' a , ' py > > ;
9395
94- fn validate_bool ( & self , strict : bool ) -> ValResult < ValidationMatch < bool > > ;
96+ fn validate_bool ( & self , strict : bool ) -> ValMatch < bool > ;
9597
96- fn validate_int ( & self , strict : bool ) -> ValResult < ValidationMatch < EitherInt < ' _ > > > ;
98+ fn validate_int ( & self , strict : bool ) -> ValMatch < EitherInt < ' _ > > ;
9799
98100 fn exact_int ( & self ) -> ValResult < EitherInt < ' _ > > {
99101 self . validate_int ( true ) . and_then ( |val_match| {
@@ -113,7 +115,7 @@ pub trait Input<'py>: fmt::Debug + ToPyObject {
113115 } )
114116 }
115117
116- fn validate_float ( & self , strict : bool ) -> ValResult < ValidationMatch < EitherFloat < ' _ > > > ;
118+ fn validate_float ( & self , strict : bool ) -> ValMatch < EitherFloat < ' _ > > ;
117119
118120 fn validate_decimal ( & self , strict : bool , py : Python < ' py > ) -> ValResult < Bound < ' py , PyAny > > {
119121 if strict {
@@ -145,18 +147,11 @@ pub trait Input<'py>: fmt::Debug + ToPyObject {
145147 self . validate_dict ( strict)
146148 }
147149
148- fn validate_list < ' a > ( & ' a self , strict : bool ) -> ValResult < GenericIterable < ' a , ' py > > {
149- if strict {
150- self . strict_list ( )
151- } else {
152- self . lax_list ( )
153- }
154- }
155- fn strict_list < ' a > ( & ' a self ) -> ValResult < GenericIterable < ' a , ' py > > ;
156- #[ cfg_attr( has_coverage_attribute, coverage( off) ) ]
157- fn lax_list < ' a > ( & ' a self ) -> ValResult < GenericIterable < ' a , ' py > > {
158- self . strict_list ( )
159- }
150+ type List < ' a > : Iterable < ' py > + AsPyList < ' py >
151+ where
152+ Self : ' a ;
153+
154+ fn validate_list ( & self , strict : bool ) -> ValMatch < Self :: List < ' _ > > ;
160155
161156 fn validate_tuple < ' a > ( & ' a self , strict : bool ) -> ValResult < GenericIterable < ' a , ' py > > {
162157 if strict {
@@ -201,25 +196,25 @@ pub trait Input<'py>: fmt::Debug + ToPyObject {
201196
202197 fn validate_iter ( & self ) -> ValResult < GenericIterator > ;
203198
204- fn validate_date ( & self , strict : bool ) -> ValResult < ValidationMatch < EitherDate < ' py > > > ;
199+ fn validate_date ( & self , strict : bool ) -> ValMatch < EitherDate < ' py > > ;
205200
206201 fn validate_time (
207202 & self ,
208203 strict : bool ,
209204 microseconds_overflow_behavior : speedate:: MicrosecondsPrecisionOverflowBehavior ,
210- ) -> ValResult < ValidationMatch < EitherTime < ' py > > > ;
205+ ) -> ValMatch < EitherTime < ' py > > ;
211206
212207 fn validate_datetime (
213208 & self ,
214209 strict : bool ,
215210 microseconds_overflow_behavior : speedate:: MicrosecondsPrecisionOverflowBehavior ,
216- ) -> ValResult < ValidationMatch < EitherDateTime < ' py > > > ;
211+ ) -> ValMatch < EitherDateTime < ' py > > ;
217212
218213 fn validate_timedelta (
219214 & self ,
220215 strict : bool ,
221216 microseconds_overflow_behavior : speedate:: MicrosecondsPrecisionOverflowBehavior ,
222- ) -> ValResult < ValidationMatch < EitherTimedelta < ' py > > > ;
217+ ) -> ValMatch < EitherTimedelta < ' py > > ;
223218}
224219
225220/// The problem to solve here is that iterating collections often returns owned
@@ -238,3 +233,42 @@ impl<'py, T: Input<'py> + ?Sized> BorrowInput<'py> for &'_ T {
238233 self
239234 }
240235}
236+
237+ pub enum Never { }
238+
239+ // Pairs with Iterable below
240+ pub trait ConsumeIterator < T > {
241+ type Output ;
242+ fn consume_iterator ( self , iterator : impl Iterator < Item = T > ) -> Self :: Output ;
243+ }
244+
245+ // This slightly awkward trait is used to define types which can be iterable. This formulation
246+ // arises because the Python enums have several different underlying iterator types, and we want to
247+ // be able to dispatch over each of them without overhead.
248+ pub trait Iterable < ' py > {
249+ type Input : BorrowInput < ' py > ;
250+ fn len ( & self ) -> Option < usize > ;
251+ fn iterate < R > ( self , consumer : impl ConsumeIterator < PyResult < Self :: Input > , Output = R > ) -> ValResult < R > ;
252+ }
253+
254+ // Necessary for inputs which don't support certain types, e.g. String -> list
255+ impl < ' py > Iterable < ' py > for Never {
256+ type Input = Bound < ' py , PyAny > ; // Doesn't really matter what this is
257+ fn len ( & self ) -> Option < usize > {
258+ unreachable ! ( )
259+ }
260+ fn iterate < R > ( self , _consumer : impl ConsumeIterator < PyResult < Self :: Input > , Output = R > ) -> ValResult < R > {
261+ unreachable ! ( )
262+ }
263+ }
264+
265+ // Optimization pathway for inputs which are already python lists
266+ pub trait AsPyList < ' py > : Iterable < ' py > {
267+ fn as_py_list ( & self ) -> Option < & Bound < ' py , PyList > > ;
268+ }
269+
270+ impl < ' py > AsPyList < ' py > for Never {
271+ fn as_py_list ( & self ) -> Option < & Bound < ' py , PyList > > {
272+ unreachable ! ( )
273+ }
274+ }
0 commit comments