1+ use quote:: ToTokens ;
12use stellar_xdr:: curr as stellar_xdr;
23use stellar_xdr:: {
34 ScSpecTypeBytesN , ScSpecTypeDef , ScSpecTypeMap , ScSpecTypeOption , ScSpecTypeResult ,
@@ -21,36 +22,53 @@ pub const BN254_FP_SERIALIZED_SIZE: u32 = 32;
2122pub const BN254_G1_SERIALIZED_SIZE : u32 = BN254_FP_SERIALIZED_SIZE * 2 ; // 64
2223pub const BN254_G2_SERIALIZED_SIZE : u32 = BN254_G1_SERIALIZED_SIZE * 2 ; // 128
2324
24- /// Checks if a type corresponds to a user-defined type (UDT) that can be used in the XDR spec safely.
25- /// Returns without error if the type is OK to use as a UDT spec name.
25+ /// Checks if an `ident` and `generics` input type maps to a user-defined type (UDT).
2626///
27- /// This function is used to check if a user-defined type input maps to a UDT. Otherwise,
28- /// the identifier risks collision with a built-in spec type of the same name.
27+ /// Returns Ok if the input will be parsed as a UDT, and returns an Err with a message if not.
28+ ///
29+ /// When users defined types like with `#[contracttype]`, the type name must map to a UDT.
30+ /// Otherwise, the type might get mapped to a built-in soroban_sdk type instead.
2931///
3032/// ### Errors
31- /// - If `generics` has any parameters, as UDTs don't support generics
3233/// - If `ident` cannot be parsed as a Rust type
33- /// - If `ident` cannot be mapped to a type with ` map_type`
34+ /// - If `ident` cannot be mapped to a type with [ map_type]
3435/// - If the type mapped from `ident` is not a UDT
35- pub fn is_input_type_spec_safe ( ident : & Ident , generics : & Generics ) -> Result < ( ) , Error > {
36- if generics. params . len ( ) > 0 {
37- return Err ( Error :: new (
38- ident. span ( ) ,
39- "generics unsupported on user-defined types" ,
40- ) ) ;
41- }
42- let ty: Type = syn:: parse_str ( & ident. to_string ( ) ) . map_err ( |e| {
36+ /// - If `generics` has any parameters, as UDTs don't support generics
37+ pub fn is_mapped_type_udt ( ident : & Ident , generics : & Generics ) -> Result < ( ) , Error > {
38+ let name = ident. to_string ( ) ;
39+ let ty: Type = syn:: parse_str ( & name) . map_err ( |e| {
4340 Error :: new (
4441 ident. span ( ) ,
45- format ! ( "type name {} cannot be used in XDR spec: {}" , ident, e) ,
42+ format ! ( "type `{}` cannot be used in XDR spec: {}" , ident, e) ,
4643 )
4744 } ) ?;
4845 match map_type ( & ty, false , false ) {
49- Ok ( ScSpecTypeDef :: Udt ( _) ) => Ok ( ( ) ) ,
50- _ => Err ( Error :: new (
51- ident. span ( ) ,
52- format ! ( "type name `{}` conflicts with a soroban_sdk type" , ident) ,
53- ) ) ,
46+ Ok ( ScSpecTypeDef :: Udt ( _) ) => {
47+ // `ty` does not contain the generics, so check manually here
48+ if generics. params . len ( ) > 0 {
49+ Err ( Error :: new (
50+ ident. span ( ) ,
51+ format ! ( "type `{}` contains generics `{}`, which are not supported for user-defined types" , ident, generics. params. to_token_stream( ) ) ,
52+ ) )
53+ } else {
54+ Ok ( ( ) )
55+ }
56+ }
57+ _ => {
58+ // Check if the error originated from the UDT-arm of `map_type`
59+ let _ = ScSpecTypeDef :: Udt ( ScSpecTypeUdt {
60+ name : name. try_into ( ) . map_err ( |e| {
61+ Error :: new (
62+ ident. span ( ) ,
63+ format ! ( "type `{}` cannot be used in XDR spec: {}" , ident, e) ,
64+ )
65+ } ) ?,
66+ } ) ;
67+ Err ( Error :: new (
68+ ident. span ( ) ,
69+ format ! ( "type `{}` conflicts with a soroban_sdk type and cannot be used as a user-defined type" , ident) ,
70+ ) )
71+ }
5472 }
5573}
5674
@@ -157,7 +175,7 @@ pub fn map_type(t: &Type, allow_ref: bool, allow_hash: bool) -> Result<ScSpecTyp
157175 name : s. try_into ( ) . map_err ( |e| {
158176 Error :: new (
159177 t. span ( ) ,
160- format ! ( "Udt name {:?} cannot be used in XDR spec: {}" , s, e) ,
178+ format ! ( "type `{}` cannot be used in XDR spec: {}" , s, e) ,
161179 )
162180 } ) ?,
163181 } ) ) ,
@@ -376,56 +394,79 @@ mod test {
376394 }
377395
378396 #[ test]
379- fn test_is_input_type_spec_safe_non_udt_errors ( ) {
397+ fn test_is_mapped_type_udt_sdk_type_errors ( ) {
380398 let input: DeriveInput = parse_quote ! (
381399 struct Address {
382400 pub key: [ u8 ; 32 ] ,
383401 }
384402 ) ;
385- is_input_type_spec_safe ( & input. ident , & input. generics )
386- . expect_err ( "type name `Address` conflicts with a soroban_sdk type" ) ;
403+ let err = is_mapped_type_udt ( & input. ident , & input. generics ) . unwrap_err ( ) ;
404+ assert_eq ! (
405+ err. to_string( ) ,
406+ "type `Address` conflicts with a soroban_sdk type and cannot be used as a user-defined type"
407+ ) ;
387408 }
388409
389410 #[ test]
390- fn test_is_input_type_spec_safe_unique_generic_type_errors ( ) {
411+ fn test_is_mapped_type_udt_unique_generic_type_errors ( ) {
391412 let input: DeriveInput = parse_quote ! (
392- struct GenericType <T > {
413+ struct GenericType <A , B > {
393414 pub key: T ,
394415 }
395416 ) ;
396- is_input_type_spec_safe ( & input. ident , & input. generics )
397- . expect_err ( " generics unsupported on user-defined types") ;
417+ let err = is_mapped_type_udt ( & input. ident , & input. generics ) . unwrap_err ( ) ;
418+ assert_eq ! ( err . to_string ( ) , "type `GenericType` contains generics `A , B`, which are not supported for user-defined types") ;
398419 }
399420
400421 #[ test]
401- fn test_is_input_type_spec_safe_generic_type_errors ( ) {
422+ fn test_is_mapped_type_udt_sdk_generic_type_errors ( ) {
402423 let input: DeriveInput = parse_quote ! (
403424 struct BytesN <T > {
404425 pub key: T ,
405426 }
406427 ) ;
407- is_input_type_spec_safe ( & input. ident , & input. generics )
408- . expect_err ( "generics unsupported on user-defined types" ) ;
428+ let err = is_mapped_type_udt ( & input. ident , & input. generics ) . unwrap_err ( ) ;
429+ assert_eq ! (
430+ err. to_string( ) ,
431+ "type `BytesN` conflicts with a soroban_sdk type and cannot be used as a user-defined type"
432+ ) ;
409433 }
410434
411435 #[ test]
412- fn test_is_input_type_spec_safe_generic_no_params_errors ( ) {
436+ fn test_is_mapped_type_udt_sdk_generic_no_params_errors ( ) {
413437 let input: DeriveInput = parse_quote ! (
414438 struct BytesN {
415439 pub key: [ u8 ; 32 ] ,
416440 }
417441 ) ;
418- is_input_type_spec_safe ( & input. ident , & input. generics )
419- . expect_err ( "type name `BytesN` conflicts with a soroban_sdk type" ) ;
442+ let err = is_mapped_type_udt ( & input. ident , & input. generics ) . unwrap_err ( ) ;
443+ assert_eq ! (
444+ err. to_string( ) ,
445+ "type `BytesN` conflicts with a soroban_sdk type and cannot be used as a user-defined type"
446+ ) ;
447+ }
448+
449+ #[ test]
450+ fn test_is_mapped_type_udt_unique_xdr_error ( ) {
451+ let input: DeriveInput = parse_quote ! (
452+ struct MyTypeIsOverSixtyCharactersLongAndShouldFailToCompileDueToThat {
453+ pub key: [ u8 ; 32 ] ,
454+ }
455+ ) ;
456+ let err = is_mapped_type_udt ( & input. ident , & input. generics ) . unwrap_err ( ) ;
457+ assert_eq ! (
458+ err. to_string( ) ,
459+ "type `MyTypeIsOverSixtyCharactersLongAndShouldFailToCompileDueToThat` cannot be used in XDR spec: xdr value max length exceeded"
460+ ) ;
420461 }
421462
422463 #[ test]
423- fn test_is_input_type_spec_safe_unique_ok ( ) {
464+ fn test_is_mapped_type_udt_unique_ok ( ) {
424465 let input: DeriveInput = parse_quote ! (
425466 struct MyType {
426467 pub key: [ u8 ; 32 ] ,
427468 }
428469 ) ;
429- assert ! ( is_input_type_spec_safe ( & input. ident, & input. generics) . is_ok( ) ) ;
470+ assert ! ( is_mapped_type_udt ( & input. ident, & input. generics) . is_ok( ) ) ;
430471 }
431472}
0 commit comments