@@ -197,21 +197,35 @@ pub(crate) trait DateFieldsResolver: Calendar {
197197 }
198198}
199199
200- #[ derive( Debug ) ]
201- struct PreDateFields {
202- extended_year : i32 ,
203- validated_month_code : Option < Month > ,
204- ordinal_month : Option < u8 > ,
205- day : u8 ,
200+ #[ derive( Copy , Clone ) ]
201+ enum ValidatedYearInput < ' a > {
202+ Extended ( i32 ) ,
203+ Era ( & ' a [ u8 ] , i32 ) ,
204+ EcmaReference { month : Month , day : u8 } ,
205+ }
206+
207+ #[ derive( Copy , Clone ) ]
208+ enum ValidatedMonthInput {
209+ Resolve ( Month ) ,
210+ UseOrdinal ( u8 ) ,
206211}
207212
213+ struct Preparation < ' a > {
214+ year_input : ValidatedYearInput < ' a > ,
215+ month_input : ValidatedMonthInput ,
216+ day : u8 ,
217+ overflow : Overflow ,
218+ check_era_consistency : Option < i32 > ,
219+ check_month_consistency : Option < u8 > ,
220+ }
208221
209- fn preprocess_date_fields (
210- fields : & DateFields ,
211- missing : MissingFieldsStrategy ,
212- ) -> Result < PreDateFields , DateFromFieldsError > {
222+ #[ inline( never) ]
223+ fn prepare_from_fields < ' a > (
224+ fields : & ' a DateFields ,
225+ options : DateFromFieldsOptions ,
226+ ) -> Result < Preparation < ' a > , DateFromFieldsError > {
227+ let missing = options. missing_fields_strategy . unwrap_or_default ( ) ;
213228
214- // day
215229 let day = match fields. day {
216230 Some ( d) => d,
217231 None => match missing {
@@ -230,93 +244,85 @@ fn preprocess_date_fields(
230244 return Err ( DateFromFieldsError :: NotEnoughFields ) ;
231245 }
232246
233- let validated_month_code = match fields. month_code {
234- Some ( mc) => Some ( Month :: try_from_utf8 ( mc) ?) ,
235- None => None ,
247+ let validated_month_code = fields. month_code . map ( Month :: try_from_utf8) . transpose ( ) ?;
248+
249+ let ( year_input, check_era_consistency) = match ( fields. era , fields. era_year ) {
250+ ( None , None ) => match fields. extended_year {
251+ Some ( y) => {
252+ let checked = range_check ( y, "year" , VALID_YEAR_RANGE ) ?;
253+ ( ValidatedYearInput :: Extended ( checked) , None )
254+ }
255+ None => match missing {
256+ MissingFieldsStrategy :: Reject => return Err ( DateFromFieldsError :: NotEnoughFields ) ,
257+ MissingFieldsStrategy :: Ecma => match ( validated_month_code, fields. ordinal_month ) {
258+ ( Some ( mc) , None ) => {
259+ ( ValidatedYearInput :: EcmaReference { month : mc, day } , None )
260+ }
261+ _ => return Err ( DateFromFieldsError :: NotEnoughFields ) ,
262+ } ,
263+ } ,
264+ } ,
265+ ( Some ( era) , Some ( era_year) ) => {
266+ let checked_era = range_check ( era_year. to_extended_year ( ) , "year" , VALID_YEAR_RANGE ) ?;
267+ (
268+ ValidatedYearInput :: Era ( era, checked_era) ,
269+ fields. extended_year ,
270+ )
271+ }
272+ _ => return Err ( DateFromFieldsError :: NotEnoughFields ) ,
273+ } ;
274+
275+ let month_input = if let Some ( mc) = validated_month_code {
276+ ValidatedMonthInput :: Resolve ( mc)
277+ } else {
278+ ValidatedMonthInput :: UseOrdinal ( fields. ordinal_month . unwrap_or ( 1 ) )
236279 } ;
237280
238-
239- let extended_year = fields. extended_year . unwrap_or ( 0 ) ;
281+ let check_month_consistency = if validated_month_code. is_some ( ) {
282+ fields. ordinal_month
283+ } else {
284+ None
285+ } ;
240286
241- Ok ( PreDateFields {
242- extended_year,
243- validated_month_code,
244- ordinal_month : fields. ordinal_month ,
287+ Ok ( Preparation {
288+ year_input,
289+ month_input,
245290 day,
291+ overflow : options. overflow . unwrap_or_default ( ) ,
292+ check_era_consistency,
293+ check_month_consistency,
246294 } )
247295}
248296
249- struct ResolvedFields {
250- extended_year : i32 ,
251- month_code : Option < Month > ,
252- ordinal_month : Option < u8 > ,
253- day : u8 ,
254- }
255-
256- fn resolve_fields_non_generic (
257- fields : & DateFields ,
258- pre : PreDateFields ,
259- missing : MissingFieldsStrategy ,
260- ) -> Result < ResolvedFields , DateFromFieldsError > {
261-
262- let day = pre. day ;
263-
264-
265- let month_code = pre. validated_month_code ;
266- let ordinal_month = pre. ordinal_month ;
267-
268- if month_code. is_none ( ) && ordinal_month. is_none ( ) {
269- return Err ( DateFromFieldsError :: NotEnoughFields ) ;
297+ #[ inline( never) ]
298+ fn finalize_from_fields (
299+ computed_year : i32 ,
300+ computed_month : u8 ,
301+ max_month : u8 ,
302+ max_day : u8 ,
303+ prep : & Preparation ,
304+ ) -> Result < ( u8 , u8 ) , DateFromFieldsError > {
305+ if let Some ( req_ext) = prep. check_era_consistency {
306+ if computed_year != req_ext {
307+ return Err ( DateFromFieldsError :: InconsistentYear ) ;
308+ }
270309 }
271310
272-
311+ range_check ( computed_year , "extended_year" , VALID_YEAR_RANGE ) ? ;
273312
274- let extended_year = match ( fields. era , fields. era_year ) {
275- ( None , None ) => {
276- match fields. extended_year {
277- Some ( y) => y,
278- None => match missing {
279- MissingFieldsStrategy :: Reject => {
280- return Err ( DateFromFieldsError :: NotEnoughFields )
281- }
282- MissingFieldsStrategy :: Ecma => {
283- // Valid only if month_code is present AND ordinal_month is absent
284- match ( fields. month_code , fields. ordinal_month ) {
285- ( Some ( _) , None ) => {
286-
287- 0
288- }
289- _ => return Err ( DateFromFieldsError :: NotEnoughFields ) ,
290- }
291- }
292- }
293- }
294- }
295-
296- ( Some ( _era) , Some ( era_year) ) => {
297- if let Some ( ext) = fields. extended_year {
298- if ext != era_year. to_extended_year ( ) {
299- return Err ( DateFromFieldsError :: InconsistentYear ) ;
300- }
301- }
302- era_year. to_extended_year ( )
313+ if let Some ( expected) = prep. check_month_consistency {
314+ if computed_month != expected {
315+ return Err ( DateFromFieldsError :: InconsistentMonth ) ;
303316 }
317+ }
304318
305- ( Some ( _) , None ) | ( None , Some ( _) ) => {
306- return Err ( DateFromFieldsError :: NotEnoughFields )
307- }
308- } ;
319+ let final_month =
320+ range_check_with_overflow ( computed_month, "month" , 1 ..=max_month, prep. overflow ) ?;
321+ let final_day = range_check_with_overflow ( prep. day , "day" , 1 ..=max_day, prep. overflow ) ?;
309322
310- Ok ( ResolvedFields {
311- extended_year,
312- month_code,
313- ordinal_month,
314- day,
315- } )
323+ Ok ( ( final_month, final_day) )
316324}
317325
318-
319-
320326impl < C : DateFieldsResolver > ArithmeticDate < C > {
321327 pub ( crate ) fn year ( self ) -> C :: YearInfo {
322328 C :: YearInfo :: unpack_year ( self . 0 )
@@ -377,37 +383,37 @@ impl<C: DateFieldsResolver> ArithmeticDate<C> {
377383 options : DateFromFieldsOptions ,
378384 calendar : & C ,
379385 ) -> Result < Self , DateFromFieldsError > {
380- let missing = options . missing_fields_strategy . unwrap_or_default ( ) ;
386+ let prep = prepare_from_fields ( & fields , options ) ? ;
381387
382- let pre = preprocess_date_fields ( & fields, missing) ?;
383- let resolved = resolve_fields_non_generic ( & fields, pre, missing) ?;
388+ let year_info = match prep. year_input {
389+ ValidatedYearInput :: Extended ( y) => calendar. year_info_from_extended ( y) ,
390+ ValidatedYearInput :: Era ( era_slice, y) => calendar. year_info_from_era ( era_slice, y) ?,
391+ ValidatedYearInput :: EcmaReference { month, day } => {
392+ calendar. reference_year_from_month_day ( month, day) ?
393+ }
394+ } ;
384395
385- let year = calendar. year_info_from_extended ( resolved. extended_year ) ;
386- let month = match resolved. month_code {
387- Some ( mc) => calendar. ordinal_from_month ( year, mc, options) ?,
388- None => resolved. ordinal_month . unwrap ( ) ,
396+ let raw_month = match prep. month_input {
397+ ValidatedMonthInput :: Resolve ( mc) => {
398+ calendar. ordinal_from_month ( year_info, mc, options) ?
399+ }
400+ ValidatedMonthInput :: UseOrdinal ( ord) => ord,
389401 } ;
390- let day = resolved. day ;
391402
392- let month = range_check_with_overflow (
393- month,
394- "month" ,
395- 1 ..=C :: months_in_provided_year ( year) ,
396- options. overflow . unwrap_or_default ( ) ,
397- ) ?;
403+ let max_month = C :: months_in_provided_year ( year_info) ;
404+ let max_day = C :: days_in_provided_month ( year_info, raw_month. clamp ( 1 , max_month) ) ;
398405
399- let day = range_check_with_overflow (
400- day,
401- "day" ,
402- 1 ..=C :: days_in_provided_month ( year, month) ,
403- options. overflow . unwrap_or_default ( ) ,
406+ let ( final_month, final_day) = finalize_from_fields (
407+ year_info. to_extended_year ( ) ,
408+ raw_month,
409+ max_month,
410+ max_day,
411+ & prep,
404412 ) ?;
405413
406- Ok ( Self :: new_unchecked ( year , month , day ) )
414+ Ok ( Self :: new_unchecked ( year_info , final_month , final_day ) )
407415 }
408416
409-
410-
411417 pub ( crate ) fn from_year_month_day (
412418 year : i32 ,
413419 month : u8 ,
0 commit comments