@@ -111,76 +111,55 @@ impl UnionValidator {
111111 let strict = state. strict_or ( self . strict ) ;
112112 let mut errors = MaybeErrors :: new ( self . custom_error . as_ref ( ) ) ;
113113
114- let mut strict_success = None ;
115- let mut lax_success = None ;
116-
117- if strict {
118- // one-pass strict validation
119- let state = & mut state. rebind_extra ( |extra| extra. strict = Some ( true ) ) ;
120- for ( choice, label) in & self . choices {
121- state. exactness = Some ( Exactness :: Exact ) ;
122- match choice. validate ( py, input, state) {
123- Ok ( success) => {
124- if state. exactness == Some ( Exactness :: Exact ) {
114+ let mut success = None ;
115+
116+ for ( choice, label) in & self . choices {
117+ let state = & mut state. rebind_extra ( |extra| {
118+ if strict {
119+ extra. strict = Some ( strict) ;
120+ }
121+ } ) ;
122+ state. exactness = Some ( Exactness :: Exact ) ;
123+ let result = choice. validate ( py, input, state) ;
124+ match result {
125+ Ok ( new_success) => match state. exactness {
126+ // exact match, return
127+ Some ( Exactness :: Exact ) => {
128+ return {
125129 // exact match, return, restore any previous exactness
126130 state. exactness = old_exactness;
127- return Ok ( success) ;
128- } else if strict_success. is_none ( ) {
129- // remember first success for later as a fallback
130- // we know this must have been strict, because we ran strict validation
131- strict_success = Some ( success) ;
132- }
131+ Ok ( new_success)
132+ } ;
133133 }
134- Err ( ValError :: LineErrors ( lines) ) => {
135- // if we don't yet know this validation will succeed, record the error
136- if strict_success. is_none ( ) {
137- errors. push ( choice, label. as_deref ( ) , lines) ;
134+ _ => {
135+ // success should always have an exactness
136+ debug_assert_ne ! ( state. exactness, None ) ;
137+ let new_exactness = state. exactness . unwrap_or ( Exactness :: Lax ) ;
138+ // if the new result has higher exactness than the current success, replace it
139+ if success
140+ . as_ref ( )
141+ . map_or ( true , |( _, current_exactness) | * current_exactness < new_exactness)
142+ {
143+ // TODO: is there a possible optimization here, where once there has
144+ // been one success, we turn on strict mode, to avoid unnecessary
145+ // coercions for further validation?
146+ success = Some ( ( new_success, new_exactness) ) ;
138147 }
139148 }
140- otherwise => return otherwise,
141- }
142- }
143- } else {
144- // one-pass lax validation
145- for ( choice, label) in & self . choices {
146- state. exactness = Some ( Exactness :: Exact ) ;
147- let result = choice. validate ( py, input, state) ;
148- match result {
149- Ok ( success) => match state. exactness {
150- // exact match, return
151- Some ( Exactness :: Exact ) => return Ok ( success) ,
152- Some ( Exactness :: Strict ) => {
153- if strict_success. is_none ( ) {
154- strict_success = Some ( success) ;
155- }
156- }
157- _ => {
158- // success should always have an exactness
159- debug_assert_eq ! ( state. exactness, Some ( Exactness :: Lax ) ) ;
160- if lax_success. is_none ( ) {
161- lax_success = Some ( success) ;
162- }
163- }
164- } ,
165- Err ( ValError :: LineErrors ( lines) ) => {
166- // if we don't yet know this validation will succeed, record the error
167- if strict_success. is_none ( ) && lax_success. is_none ( ) {
168- errors. push ( choice, label. as_deref ( ) , lines) ;
169- }
149+ } ,
150+ Err ( ValError :: LineErrors ( lines) ) => {
151+ // if we don't yet know this validation will succeed, record the error
152+ if success. is_none ( ) {
153+ errors. push ( choice, label. as_deref ( ) , lines) ;
170154 }
171- otherwise => return otherwise,
172155 }
156+ otherwise => return otherwise,
173157 }
174158 }
175159 state. exactness = old_exactness;
176160
177- if let Some ( success) = strict_success {
178- state. floor_exactness ( Exactness :: Strict ) ;
179- return Ok ( success) ;
180- }
181-
182- if let Some ( success) = lax_success {
183- state. floor_exactness ( Exactness :: Lax ) ;
161+ if let Some ( ( success, exactness) ) = success {
162+ state. floor_exactness ( exactness) ;
184163 return Ok ( success) ;
185164 }
186165
0 commit comments