@@ -44,7 +44,7 @@ macro_rules! define_rust_targets {
44
44
}
45
45
46
46
impl RustTarget {
47
- fn minor( self ) -> Option <u64 > {
47
+ const fn minor( self ) -> Option <u64 > {
48
48
match self {
49
49
$( Self :: $variant => Some ( $minor) , ) *
50
50
Self :: Nightly => None
@@ -136,15 +136,15 @@ define_rust_targets! {
136
136
Stable_1_0 ( 0 ) => { } ,
137
137
}
138
138
139
- /// Latest stable release of Rust
139
+ /// Latest stable release of Rust that is supported by bindgen
140
140
pub const LATEST_STABLE_RUST : RustTarget = {
141
141
// FIXME: replace all this code by
142
142
// ```
143
143
// RustTarget::stable_releases()
144
144
// .into_iter()
145
145
// .max_by_key(|(_, m)| m)
146
146
// .map(|(t, _)| t)
147
- // .unwrap_or(RustTarget::Nightly )
147
+ // .unwrap( )
148
148
// ```
149
149
// once those operations can be used in constants.
150
150
let targets = RustTarget :: stable_releases ( ) ;
@@ -170,6 +170,42 @@ pub const LATEST_STABLE_RUST: RustTarget = {
170
170
}
171
171
} ;
172
172
173
+ /// Earliest stable release of Rust that is supported by bindgen
174
+ pub const EARLIEST_STABLE_RUST : RustTarget = {
175
+ // FIXME: replace all this code by
176
+ // ```
177
+ // RustTarget::stable_releases()
178
+ // .into_iter()
179
+ // .min_by_key(|(_, m)| m)
180
+ // .map(|(t, _)| t)
181
+ // .unwrap_or(LATEST_STABLE_RUST)
182
+ // ```
183
+ // once those operations can be used in constants.
184
+ let targets = RustTarget :: stable_releases ( ) ;
185
+
186
+ let mut i = 0 ;
187
+ let mut earliest_target = None ;
188
+ let Some ( mut earliest_minor) = LATEST_STABLE_RUST . minor ( ) else {
189
+ unreachable ! ( )
190
+ } ;
191
+
192
+ while i < targets. len ( ) {
193
+ let ( target, minor) = targets[ i] ;
194
+
195
+ if earliest_minor > minor {
196
+ earliest_minor = minor;
197
+ earliest_target = Some ( target) ;
198
+ }
199
+
200
+ i += 1 ;
201
+ }
202
+
203
+ match earliest_target {
204
+ Some ( target) => target,
205
+ None => unreachable ! ( ) ,
206
+ }
207
+ } ;
208
+
173
209
impl Default for RustTarget {
174
210
fn default ( ) -> Self {
175
211
LATEST_STABLE_RUST
@@ -193,28 +229,62 @@ impl Ord for RustTarget {
193
229
}
194
230
}
195
231
232
+ fn invalid_input < T > ( input : & str , msg : impl std:: fmt:: Display ) -> io:: Result < T > {
233
+ Err ( io:: Error :: new (
234
+ io:: ErrorKind :: InvalidInput ,
235
+ format ! ( "\" {input}\" is not a valid Rust target, {msg}" ) ,
236
+ ) )
237
+ }
238
+
196
239
impl FromStr for RustTarget {
197
240
type Err = io:: Error ;
198
241
199
- fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
200
- if s == "nightly" {
242
+ fn from_str ( input : & str ) -> Result < Self , Self :: Err > {
243
+ if input == "nightly" {
201
244
return Ok ( Self :: Nightly ) ;
202
245
}
203
246
204
- if let Some ( ( "1" , str_minor) ) = s. split_once ( '.' ) {
205
- if let Ok ( minor) = str_minor. parse :: < u64 > ( ) {
206
- for ( target, target_minor) in Self :: stable_releases ( ) {
207
- if minor == target_minor {
208
- return Ok ( target) ;
209
- }
210
- }
211
- }
247
+ let Some ( ( major_str, tail) ) = input. split_once ( '.' ) else {
248
+ return invalid_input ( input, "accepted values are of the form \" 1.71\" , \" 1.71.1\" or \" nightly\" ." ) ;
249
+ } ;
250
+
251
+ if major_str != "1" {
252
+ return invalid_input (
253
+ input,
254
+ "The largest major version of Rust released is \" 1\" " ,
255
+ ) ;
212
256
}
213
257
214
- Err ( io:: Error :: new (
215
- io:: ErrorKind :: InvalidInput ,
216
- "Got an invalid Rust target. Accepted values are of the form \" 1.71\" or \" nightly\" ."
217
- ) )
258
+ // We ignore the patch version number as they only include backwards compatible bug fixes.
259
+ let ( minor, _patch) = match tail. split_once ( '.' ) {
260
+ Some ( ( minor_str, patch_str) ) => {
261
+ let Ok ( minor) = minor_str. parse :: < u64 > ( ) else {
262
+ return invalid_input ( input, "the minor version number must be an unsigned 64-bit integer" ) ;
263
+ } ;
264
+ let Ok ( patch) = patch_str. parse :: < u64 > ( ) else {
265
+ return invalid_input ( input, "the patch version number must be an unsigned 64-bit integer" ) ;
266
+ } ;
267
+ ( minor, patch)
268
+ }
269
+ None => {
270
+ let Ok ( minor) = tail. parse :: < u64 > ( ) else {
271
+ return invalid_input ( input, "the minor version number must be an unsigned 64-bit integer" ) ;
272
+ } ;
273
+ ( minor, 0 )
274
+ }
275
+ } ;
276
+
277
+ let Some ( target) = Self :: stable_releases ( )
278
+ . iter ( )
279
+ . filter ( |( _, target_minor) | minor >= * target_minor)
280
+ . max_by_key ( |( _, target_minor) | target_minor)
281
+ . map ( |( target, _) | target)
282
+ . cloned ( )
283
+ else {
284
+ return invalid_input ( input, format ! ( "the earliest Rust target supported by bindgen is {EARLIEST_STABLE_RUST}" ) ) ;
285
+ } ;
286
+
287
+ Ok ( target)
218
288
}
219
289
}
220
290
@@ -282,19 +352,37 @@ mod test {
282
352
}
283
353
284
354
fn test_target ( target_str : & str , target : RustTarget ) {
285
- let target_string = target. to_string ( ) ;
286
- assert_eq ! ( target_str, target_string) ;
287
- assert_eq ! ( target, RustTarget :: from_str( target_str) . unwrap( ) ) ;
355
+ assert_eq ! (
356
+ target,
357
+ target_str. parse:: <RustTarget >( ) . unwrap( ) ,
358
+ "{target_str}"
359
+ ) ;
360
+ }
361
+
362
+ fn test_invalid_target ( target_str : & str ) {
363
+ assert ! ( target_str. parse:: <RustTarget >( ) . is_err( ) , "{}" , target_str) ;
288
364
}
289
365
290
366
#[ test]
291
- fn str_to_target ( ) {
292
- test_target ( "1.0" , RustTarget :: Stable_1_0 ) ;
293
- test_target ( "1.17" , RustTarget :: Stable_1_17 ) ;
294
- test_target ( "1.19" , RustTarget :: Stable_1_19 ) ;
295
- test_target ( "1.21" , RustTarget :: Stable_1_21 ) ;
296
- test_target ( "1.25" , RustTarget :: Stable_1_25 ) ;
367
+ fn valid_targets ( ) {
297
368
test_target ( "1.71" , RustTarget :: Stable_1_71 ) ;
369
+ test_target ( "1.71.0" , RustTarget :: Stable_1_71 ) ;
370
+ test_target ( "1.71.1" , RustTarget :: Stable_1_71 ) ;
371
+ test_target ( "1.72" , RustTarget :: Stable_1_71 ) ;
372
+ test_target ( "1.73" , RustTarget :: Stable_1_73 ) ;
373
+ test_target ( "1.18446744073709551615" , LATEST_STABLE_RUST ) ;
298
374
test_target ( "nightly" , RustTarget :: Nightly ) ;
299
375
}
376
+
377
+ #[ test]
378
+ fn invalid_targets ( ) {
379
+ test_invalid_target ( "2.0" ) ;
380
+ test_invalid_target ( "1.cat" ) ;
381
+ test_invalid_target ( "1.0.cat" ) ;
382
+ test_invalid_target ( "1.18446744073709551616" ) ;
383
+ test_invalid_target ( "1.0.18446744073709551616" ) ;
384
+ test_invalid_target ( "1.-1.0" ) ;
385
+ test_invalid_target ( "1.0.-1" ) ;
386
+ test_invalid_target ( "beta" ) ;
387
+ }
300
388
}
0 commit comments