@@ -7,8 +7,10 @@ use std::iter;
77use serde:: ser:: SerializeSeq ;
88
99use crate :: definitions:: DefinitionsBuilder ;
10+ use crate :: serializers:: extra:: SerCheck ;
1011use crate :: serializers:: type_serializers:: any:: AnySerializer ;
1112use crate :: tools:: SchemaDict ;
13+ use crate :: PydanticSerializationUnexpectedValue ;
1214
1315use super :: {
1416 infer_json_key, infer_serialize, infer_to_python, py_err_se_err, BuildSerializer , CombinedSerializer , Extra ,
@@ -70,52 +72,14 @@ impl TypeSerializer for TupleSerializer {
7072 let py = value. py ( ) ;
7173
7274 let n_items = py_tuple. len ( ) ;
73- let mut py_tuple_iter = py_tuple. iter ( ) ;
7475 let mut items = Vec :: with_capacity ( n_items) ;
7576
76- macro_rules! use_serializers {
77- ( $serializers_iter: expr) => {
78- for ( index, serializer) in $serializers_iter. enumerate( ) {
79- let element = match py_tuple_iter. next( ) {
80- Some ( value) => value,
81- None => break ,
82- } ;
83- let op_next = self
84- . filter
85- . index_filter( index, include, exclude, Some ( n_items) ) ?;
86- if let Some ( ( next_include, next_exclude) ) = op_next {
87- items. push( serializer. to_python( element, next_include, next_exclude, extra) ?) ;
88- }
89- }
90- } ;
91- }
92-
93- if let Some ( variadic_item_index) = self . variadic_item_index {
94- // Need `saturating_sub` to handle items with too few elements without panicking
95- let n_variadic_items = ( n_items + 1 ) . saturating_sub ( self . serializers . len ( ) ) ;
96- let serializers_iter = self . serializers [ ..variadic_item_index]
97- . iter ( )
98- . chain ( iter:: repeat ( & self . serializers [ variadic_item_index] ) . take ( n_variadic_items) )
99- . chain ( self . serializers [ variadic_item_index + 1 ..] . iter ( ) ) ;
100- use_serializers ! ( serializers_iter) ;
101- } else {
102- use_serializers ! ( self . serializers. iter( ) ) ;
103- let mut warned = false ;
104- for ( i, element) in py_tuple_iter. enumerate ( ) {
105- if !warned {
106- extra
107- . warnings
108- . custom_warning ( "Unexpected extra items present in tuple" . to_string ( ) ) ;
109- warned = true ;
110- }
111- let op_next =
112- self . filter
113- . index_filter ( i + self . serializers . len ( ) , include, exclude, Some ( n_items) ) ?;
114- if let Some ( ( next_include, next_exclude) ) = op_next {
115- items. push ( AnySerializer . to_python ( element, next_include, next_exclude, extra) ?) ;
116- }
117- }
118- } ;
77+ self . for_each_tuple_item_and_serializer ( py_tuple, include, exclude, extra, |entry| {
78+ entry
79+ . serializer
80+ . to_python ( entry. item , entry. include , entry. exclude , extra)
81+ . map ( |item| items. push ( item) )
82+ } ) ??;
11983
12084 match extra. mode {
12185 SerMode :: Json => Ok ( PyList :: new ( py, items) . into_py ( py) ) ,
@@ -132,35 +96,14 @@ impl TypeSerializer for TupleSerializer {
13296 fn json_key < ' py > ( & self , key : & ' py PyAny , extra : & Extra ) -> PyResult < Cow < ' py , str > > {
13397 match key. downcast :: < PyTuple > ( ) {
13498 Ok ( py_tuple) => {
135- let mut py_tuple_iter = py_tuple. iter ( ) ;
136-
13799 let mut key_builder = KeyBuilder :: new ( ) ;
138100
139- let n_items = py_tuple. len ( ) ;
140-
141- macro_rules! use_serializers {
142- ( $serializers_iter: expr) => {
143- for serializer in $serializers_iter {
144- let element = match py_tuple_iter. next( ) {
145- Some ( value) => value,
146- None => break ,
147- } ;
148- key_builder. push( & serializer. json_key( element, extra) ?) ;
149- }
150- } ;
151- }
152-
153- if let Some ( variadic_item_index) = self . variadic_item_index {
154- // Need `saturating_sub` to handle items with too few elements without panicking
155- let n_variadic_items = ( n_items + 1 ) . saturating_sub ( self . serializers . len ( ) ) ;
156- let serializers_iter = self . serializers [ ..variadic_item_index]
157- . iter ( )
158- . chain ( iter:: repeat ( & self . serializers [ variadic_item_index] ) . take ( n_variadic_items) )
159- . chain ( self . serializers [ variadic_item_index + 1 ..] . iter ( ) ) ;
160- use_serializers ! ( serializers_iter) ;
161- } else {
162- use_serializers ! ( self . serializers. iter( ) ) ;
163- } ;
101+ self . for_each_tuple_item_and_serializer ( py_tuple, None , None , extra, |entry| {
102+ entry
103+ . serializer
104+ . json_key ( entry. item , extra)
105+ . map ( |key| key_builder. push ( & key) )
106+ } ) ??;
164107
165108 Ok ( Cow :: Owned ( key_builder. finish ( ) ) )
166109 }
@@ -184,63 +127,18 @@ impl TypeSerializer for TupleSerializer {
184127 let py_tuple: & PyTuple = py_tuple. downcast ( ) . map_err ( py_err_se_err) ?;
185128
186129 let n_items = py_tuple. len ( ) ;
187- let mut py_tuple_iter = py_tuple. iter ( ) ;
188130 let mut seq = serializer. serialize_seq ( Some ( n_items) ) ?;
189131
190- macro_rules! use_serializers {
191- ( $serializers_iter: expr) => {
192- for ( index, serializer) in $serializers_iter. enumerate( ) {
193- let element = match py_tuple_iter. next( ) {
194- Some ( value) => value,
195- None => break ,
196- } ;
197- let op_next = self
198- . filter
199- . index_filter( index, include, exclude, Some ( n_items) )
200- . map_err( py_err_se_err) ?;
201- if let Some ( ( next_include, next_exclude) ) = op_next {
202- let item_serialize =
203- PydanticSerializer :: new( element, serializer, next_include, next_exclude, extra) ;
204- seq. serialize_element( & item_serialize) ?;
205- }
206- }
207- } ;
208- }
209-
210- if let Some ( variadic_item_index) = self . variadic_item_index {
211- // Need `saturating_sub` to handle items with too few elements without panicking
212- let n_variadic_items = ( n_items + 1 ) . saturating_sub ( self . serializers . len ( ) ) ;
213- let serializers_iter = self . serializers [ ..variadic_item_index]
214- . iter ( )
215- . chain ( iter:: repeat ( & self . serializers [ variadic_item_index] ) . take ( n_variadic_items) )
216- . chain ( self . serializers [ variadic_item_index + 1 ..] . iter ( ) ) ;
217- use_serializers ! ( serializers_iter) ;
218- } else {
219- use_serializers ! ( self . serializers. iter( ) ) ;
220- let mut warned = false ;
221- for ( i, element) in py_tuple_iter. enumerate ( ) {
222- if !warned {
223- extra
224- . warnings
225- . custom_warning ( "Unexpected extra items present in tuple" . to_string ( ) ) ;
226- warned = true ;
227- }
228- let op_next = self
229- . filter
230- . index_filter ( i + self . serializers . len ( ) , include, exclude, Some ( n_items) )
231- . map_err ( py_err_se_err) ?;
232- if let Some ( ( next_include, next_exclude) ) = op_next {
233- let item_serialize = PydanticSerializer :: new (
234- element,
235- & CombinedSerializer :: Any ( AnySerializer ) ,
236- next_include,
237- next_exclude,
238- extra,
239- ) ;
240- seq. serialize_element ( & item_serialize) ?;
241- }
242- }
243- } ;
132+ self . for_each_tuple_item_and_serializer ( py_tuple, include, exclude, extra, |entry| {
133+ seq. serialize_element ( & PydanticSerializer :: new (
134+ entry. item ,
135+ entry. serializer ,
136+ entry. include ,
137+ entry. exclude ,
138+ extra,
139+ ) )
140+ } )
141+ . map_err ( py_err_se_err) ??;
244142
245143 seq. end ( )
246144 }
@@ -254,6 +152,100 @@ impl TypeSerializer for TupleSerializer {
254152 fn get_name ( & self ) -> & str {
255153 & self . name
256154 }
155+
156+ fn retry_with_lax_check ( & self ) -> bool {
157+ true
158+ }
159+ }
160+
161+ struct TupleSerializerEntry < ' a , ' py > {
162+ item : & ' py PyAny ,
163+ include : Option < & ' py PyAny > ,
164+ exclude : Option < & ' py PyAny > ,
165+ serializer : & ' a CombinedSerializer ,
166+ }
167+
168+ impl TupleSerializer {
169+ /// Try to serialize each item in the tuple with the corresponding serializer.
170+ ///
171+ /// If the tuple doesn't match the length of the serializer, in strict mode, an error is returned.
172+ ///
173+ /// The error type E is the type of the error returned by the closure, which is why there are two
174+ /// levels of `Result`.
175+ fn for_each_tuple_item_and_serializer < E > (
176+ & self ,
177+ tuple : & PyTuple ,
178+ include : Option < & PyAny > ,
179+ exclude : Option < & PyAny > ,
180+ extra : & Extra ,
181+ mut f : impl for <' a , ' py > FnMut ( TupleSerializerEntry < ' a , ' py > ) -> Result < ( ) , E > ,
182+ ) -> PyResult < Result < ( ) , E > > {
183+ let n_items = tuple. len ( ) ;
184+ let mut py_tuple_iter = tuple. iter ( ) ;
185+
186+ macro_rules! use_serializers {
187+ ( $serializers_iter: expr) => {
188+ for ( index, serializer) in $serializers_iter. enumerate( ) {
189+ let element = match py_tuple_iter. next( ) {
190+ Some ( value) => value,
191+ None => break ,
192+ } ;
193+ let op_next = self . filter. index_filter( index, include, exclude, Some ( n_items) ) ?;
194+ if let Some ( ( next_include, next_exclude) ) = op_next {
195+ if let Err ( e) = f( TupleSerializerEntry {
196+ item: element,
197+ include: next_include,
198+ exclude: next_exclude,
199+ serializer,
200+ } ) {
201+ return Ok ( Err ( e) ) ;
202+ } ;
203+ }
204+ }
205+ } ;
206+ }
207+
208+ if let Some ( variadic_item_index) = self . variadic_item_index {
209+ // Need `saturating_sub` to handle items with too few elements without panicking
210+ let n_variadic_items = ( n_items + 1 ) . saturating_sub ( self . serializers . len ( ) ) ;
211+ let serializers_iter = self . serializers [ ..variadic_item_index]
212+ . iter ( )
213+ . chain ( iter:: repeat ( & self . serializers [ variadic_item_index] ) . take ( n_variadic_items) )
214+ . chain ( self . serializers [ variadic_item_index + 1 ..] . iter ( ) ) ;
215+ use_serializers ! ( serializers_iter) ;
216+ } else if extra. check == SerCheck :: Strict && n_items != self . serializers . len ( ) {
217+ return Err ( PydanticSerializationUnexpectedValue :: new_err ( Some ( format ! (
218+ "Expected {} items, but got {}" ,
219+ self . serializers. len( ) ,
220+ n_items
221+ ) ) ) ) ;
222+ } else {
223+ use_serializers ! ( self . serializers. iter( ) ) ;
224+ let mut warned = false ;
225+ for ( i, element) in py_tuple_iter. enumerate ( ) {
226+ if !warned {
227+ extra
228+ . warnings
229+ . custom_warning ( "Unexpected extra items present in tuple" . to_string ( ) ) ;
230+ warned = true ;
231+ }
232+ let op_next = self
233+ . filter
234+ . index_filter ( i + self . serializers . len ( ) , include, exclude, Some ( n_items) ) ?;
235+ if let Some ( ( next_include, next_exclude) ) = op_next {
236+ if let Err ( e) = f ( TupleSerializerEntry {
237+ item : element,
238+ include : next_include,
239+ exclude : next_exclude,
240+ serializer : & CombinedSerializer :: Any ( AnySerializer ) ,
241+ } ) {
242+ return Ok ( Err ( e) ) ;
243+ } ;
244+ }
245+ }
246+ } ;
247+ Ok ( Ok ( ( ) ) )
248+ }
257249}
258250
259251pub ( crate ) struct KeyBuilder {
0 commit comments