8
8
9
9
use {
10
10
bytemuck:: { Pod , Zeroable } ,
11
- solana_program:: { program_option:: COption , pubkey:: Pubkey } ,
11
+ solana_program:: {
12
+ program_error:: ProgramError ,
13
+ program_option:: COption ,
14
+ pubkey:: { Pubkey , PUBKEY_BYTES } ,
15
+ } ,
12
16
} ;
13
17
14
18
/// Trait for types that can be `None`.
15
19
///
16
20
/// This trait is used to indicate that a type can be `None` according to a
17
21
/// specific value.
18
- pub trait Nullable : Default + Pod {
22
+ pub trait Nullable : PartialEq + Pod + Sized {
23
+ /// Value that represents `None` for the type.
24
+ const NONE : Self ;
25
+
19
26
/// Indicates whether the value is `None` or not.
20
- fn is_none ( & self ) -> bool ;
27
+ fn is_none ( & self ) -> bool {
28
+ self == & Self :: NONE
29
+ }
21
30
22
31
/// Indicates whether the value is `Some`` value of type `T`` or not.
23
32
fn is_some ( & self ) -> bool {
@@ -66,8 +75,16 @@ impl<T: Nullable> PodOption<T> {
66
75
}
67
76
}
68
77
78
+ /// ## Safety
79
+ ///
80
+ /// `PodOption` is a transparent wrapper around a `Pod` type `T` with identical
81
+ /// data representation.
69
82
unsafe impl < T : Nullable > Pod for PodOption < T > { }
70
83
84
+ /// ## Safety
85
+ ///
86
+ /// `PodOption` is a transparent wrapper around a `Pod` type `T` with identical
87
+ /// data representation.
71
88
unsafe impl < T : Nullable > Zeroable for PodOption < T > { }
72
89
73
90
impl < T : Nullable > From < T > for PodOption < T > {
@@ -76,32 +93,33 @@ impl<T: Nullable> From<T> for PodOption<T> {
76
93
}
77
94
}
78
95
79
- impl < T : Nullable > From < Option < T > > for PodOption < T > {
80
- fn from ( from : Option < T > ) -> Self {
81
- match from {
82
- Some ( value) => PodOption ( value) ,
83
- None => PodOption ( T :: default ( ) ) ,
96
+ impl < T : Nullable > TryFrom < Option < T > > for PodOption < T > {
97
+ type Error = ProgramError ;
98
+
99
+ fn try_from ( value : Option < T > ) -> Result < Self , Self :: Error > {
100
+ match value {
101
+ Some ( value) if value. is_none ( ) => Err ( ProgramError :: InvalidArgument ) ,
102
+ Some ( value) => Ok ( PodOption ( value) ) ,
103
+ None => Ok ( PodOption ( T :: NONE ) ) ,
84
104
}
85
105
}
86
106
}
87
107
88
- impl < T : Nullable > From < COption < T > > for PodOption < T > {
89
- fn from ( from : COption < T > ) -> Self {
90
- match from {
91
- COption :: Some ( value) => PodOption ( value) ,
92
- COption :: None => PodOption ( T :: default ( ) ) ,
108
+ impl < T : Nullable > TryFrom < COption < T > > for PodOption < T > {
109
+ type Error = ProgramError ;
110
+
111
+ fn try_from ( value : COption < T > ) -> Result < Self , Self :: Error > {
112
+ match value {
113
+ COption :: Some ( value) if value. is_none ( ) => Err ( ProgramError :: InvalidArgument ) ,
114
+ COption :: Some ( value) => Ok ( PodOption ( value) ) ,
115
+ COption :: None => Ok ( PodOption ( T :: NONE ) ) ,
93
116
}
94
117
}
95
118
}
96
119
97
120
/// Implementation of `Nullable` for `Pubkey`.
98
- ///
99
- /// The implementation assumes that the default value of `Pubkey` represents
100
- /// the `None` value.
101
121
impl Nullable for Pubkey {
102
- fn is_none ( & self ) -> bool {
103
- self == & Pubkey :: default ( )
104
- }
122
+ const NONE : Self = Pubkey :: new_from_array ( [ 0u8 ; PUBKEY_BYTES ] ) ;
105
123
}
106
124
107
125
#[ cfg( test) ]
@@ -126,13 +144,38 @@ mod tests {
126
144
assert_eq ! ( values[ 1 ] , PodOption :: from( Pubkey :: default ( ) ) ) ;
127
145
128
146
let option_pubkey = Some ( sysvar:: ID ) ;
129
- let pod_option_pubkey: PodOption < Pubkey > = option_pubkey. into ( ) ;
147
+ let pod_option_pubkey: PodOption < Pubkey > = option_pubkey. try_into ( ) . unwrap ( ) ;
130
148
assert_eq ! ( pod_option_pubkey, PodOption :: from( sysvar:: ID ) ) ;
131
- assert_eq ! ( pod_option_pubkey, PodOption :: from( option_pubkey) ) ;
149
+ assert_eq ! (
150
+ pod_option_pubkey,
151
+ PodOption :: try_from( option_pubkey) . unwrap( )
152
+ ) ;
132
153
133
154
let coption_pubkey = COption :: Some ( sysvar:: ID ) ;
134
- let pod_option_pubkey: PodOption < Pubkey > = coption_pubkey. into ( ) ;
155
+ let pod_option_pubkey: PodOption < Pubkey > = coption_pubkey. try_into ( ) . unwrap ( ) ;
135
156
assert_eq ! ( pod_option_pubkey, PodOption :: from( sysvar:: ID ) ) ;
136
- assert_eq ! ( pod_option_pubkey, PodOption :: from( coption_pubkey) ) ;
157
+ assert_eq ! (
158
+ pod_option_pubkey,
159
+ PodOption :: try_from( coption_pubkey) . unwrap( )
160
+ ) ;
161
+ }
162
+
163
+ #[ test]
164
+ fn test_try_from_option ( ) {
165
+ let some_pubkey = Some ( sysvar:: ID ) ;
166
+ assert_eq ! (
167
+ PodOption :: try_from( some_pubkey) . unwrap( ) ,
168
+ PodOption ( sysvar:: ID )
169
+ ) ;
170
+
171
+ let none_pubkey = None ;
172
+ assert_eq ! (
173
+ PodOption :: try_from( none_pubkey) . unwrap( ) ,
174
+ PodOption :: from( Pubkey :: NONE )
175
+ ) ;
176
+
177
+ let invalid_option = Some ( Pubkey :: NONE ) ;
178
+ let err = PodOption :: try_from ( invalid_option) . unwrap_err ( ) ;
179
+ assert_eq ! ( err, ProgramError :: InvalidArgument ) ;
137
180
}
138
181
}
0 commit comments