@@ -381,6 +381,69 @@ struct PyClassEnumVariant<'a> {
381
381
/* currently have no more options */
382
382
}
383
383
384
+ struct PyClassEnum < ' a > {
385
+ ident : & ' a syn:: Ident ,
386
+ // The underyling representation of the enum.
387
+ // It's used to implement __int__ and __richcmp__.
388
+ // This matters when the underyling representation may not fit in `isize`.
389
+ #[ allow( unused, dead_code) ]
390
+ repr : syn:: Ident ,
391
+ variants : Vec < PyClassEnumVariant < ' a > > ,
392
+ doc : PythonDoc ,
393
+ }
394
+
395
+ impl < ' a > PyClassEnum < ' a > {
396
+ fn new ( enum_ : & ' a syn:: ItemEnum ) -> syn:: Result < Self > {
397
+ fn is_numeric_type ( t : & syn:: Ident ) -> bool {
398
+ [
399
+ "u8" , "i8" , "u16" , "i16" , "u32" , "i32" , "u64" , "i64" , "u128" , "i128" , "usize" ,
400
+ "isize" ,
401
+ ]
402
+ . iter ( )
403
+ . any ( |& s| t == s)
404
+ }
405
+ struct Reprs ( syn:: punctuated:: Punctuated < syn:: Ident , Token ! [ , ] > ) ;
406
+ impl Parse for Reprs {
407
+ fn parse ( input : ParseStream ) -> Result < Self > {
408
+ let inner = Punctuated :: parse_terminated ( input) ?;
409
+ Ok ( Self ( inner) )
410
+ }
411
+ }
412
+ let ident = & enum_. ident ;
413
+ // According to the [reference](https://doc.rust-lang.org/reference/items/enumerations.html),
414
+ // "Under the default representation, the specified discriminant is interpreted as an isize
415
+ // value", so `isize` should be enough by default.
416
+ // `cargo test` also tests the following facts:
417
+ // - Rustc emits a compile error when the user use a discriminant larger than `isize`.
418
+ // - Rustc emits a compile error when the default discriminant is larger than `isize`.
419
+ let mut repr = syn:: Ident :: new ( "isize" , proc_macro2:: Span :: call_site ( ) ) ;
420
+ for attr in & enum_. attrs {
421
+ if attr. path . is_ident ( "repr" ) {
422
+ let reprs: Reprs = attr. parse_args ( ) ?;
423
+ for r in reprs. 0 {
424
+ if is_numeric_type ( & r) {
425
+ repr = r;
426
+ break ;
427
+ }
428
+ }
429
+ }
430
+ }
431
+ let doc = utils:: get_doc ( & enum_. attrs , None ) ;
432
+
433
+ let variants = enum_
434
+ . variants
435
+ . iter ( )
436
+ . map ( extract_variant_data)
437
+ . collect :: < syn:: Result < _ > > ( ) ?;
438
+ Ok ( Self {
439
+ ident,
440
+ repr,
441
+ variants,
442
+ doc,
443
+ } )
444
+ }
445
+ }
446
+
384
447
pub fn build_py_enum (
385
448
enum_ : & syn:: ItemEnum ,
386
449
args : PyClassArgs ,
@@ -389,38 +452,32 @@ pub fn build_py_enum(
389
452
if enum_. variants . is_empty ( ) {
390
453
bail_spanned ! ( enum_. brace_token. span => "Empty enums can't be #[pyclass]." ) ;
391
454
}
392
- let variants: Vec < PyClassEnumVariant > = enum_
393
- . variants
394
- . iter ( )
395
- . map ( extract_variant_data)
396
- . collect :: < syn:: Result < _ > > ( ) ?;
397
- impl_enum ( enum_, args, variants, method_type)
455
+ let enum_ = PyClassEnum :: new ( enum_) ?;
456
+ impl_enum ( enum_, args, method_type)
398
457
}
399
458
400
459
fn impl_enum (
401
- enum_ : & syn:: ItemEnum ,
402
- attrs : PyClassArgs ,
403
- variants : Vec < PyClassEnumVariant > ,
460
+ enum_ : PyClassEnum ,
461
+ args : PyClassArgs ,
404
462
methods_type : PyClassMethodsType ,
405
463
) -> syn:: Result < TokenStream > {
406
- let enum_name = & enum_. ident ;
407
- let doc = utils:: get_doc ( & enum_. attrs , None ) ;
408
- let enum_cls = impl_enum_class ( enum_name, & attrs, variants, doc, methods_type) ?;
464
+ let enum_cls = impl_enum_class ( enum_, & args, methods_type) ?;
409
465
410
466
Ok ( quote ! {
411
467
#enum_cls
412
468
} )
413
469
}
414
470
415
471
fn impl_enum_class (
416
- cls : & syn:: Ident ,
417
- attr : & PyClassArgs ,
418
- variants : Vec < PyClassEnumVariant > ,
419
- doc : PythonDoc ,
472
+ enum_ : PyClassEnum ,
473
+ args : & PyClassArgs ,
420
474
methods_type : PyClassMethodsType ,
421
475
) -> syn:: Result < TokenStream > {
422
- let pytypeinfo = impl_pytypeinfo ( cls, attr, None ) ;
423
- let pyclass_impls = PyClassImplsBuilder :: new ( cls, attr, methods_type)
476
+ let cls = enum_. ident ;
477
+ let doc = enum_. doc ;
478
+ let variants = enum_. variants ;
479
+ let pytypeinfo = impl_pytypeinfo ( cls, args, None ) ;
480
+ let pyclass_impls = PyClassImplsBuilder :: new ( cls, args, methods_type)
424
481
. doc ( doc)
425
482
. impl_all ( ) ;
426
483
let descriptors = unit_variants_as_descriptors ( cls, variants. iter ( ) . map ( |v| v. ident ) ) ;
@@ -494,9 +551,6 @@ fn extract_variant_data(variant: &syn::Variant) -> syn::Result<PyClassEnumVarian
494
551
Fields :: Unit => & variant. ident ,
495
552
_ => bail_spanned ! ( variant. span( ) => "Currently only support unit variants." ) ,
496
553
} ;
497
- if let Some ( discriminant) = variant. discriminant . as_ref ( ) {
498
- bail_spanned ! ( discriminant. 0 . span( ) => "Currently does not support discriminats." )
499
- } ;
500
554
Ok ( PyClassEnumVariant { ident } )
501
555
}
502
556
0 commit comments