1use core::str::FromStr;
8
9use super::FileTime;
10use crate::error::ParseFileTimeError;
11
12impl FileTime {
13 #[inline]
85 pub fn from_str_radix(src: &str, radix: u32) -> Result<Self, ParseFileTimeError> {
86 u64::from_str_radix(src, radix)
87 .map_err(ParseFileTimeError::new)
88 .map(Self::new)
89 }
90}
91
92impl FromStr for FileTime {
93 type Err = ParseFileTimeError;
94
95 #[inline]
141 fn from_str(src: &str) -> Result<Self, Self::Err> {
142 Self::from_str_radix(src, 10)
143 }
144}
145
146#[cfg(test)]
147mod tests {
148 use core::{
149 error::Error,
150 num::{IntErrorKind, ParseIntError},
151 };
152
153 use super::*;
154
155 #[test]
156 fn from_str_radix() {
157 assert_eq!(
158 FileTime::from_str_radix("0", 2).unwrap(),
159 FileTime::NT_TIME_EPOCH
160 );
161 assert_eq!(
162 FileTime::from_str_radix("+0", 2).unwrap(),
163 FileTime::NT_TIME_EPOCH
164 );
165 assert_eq!(
166 FileTime::from_str_radix(
167 "110011101101100011101111011010101001111101000000000000000",
168 2
169 )
170 .unwrap(),
171 FileTime::UNIX_EPOCH
172 );
173 assert_eq!(
174 FileTime::from_str_radix(
175 "+110011101101100011101111011010101001111101000000000000000",
176 2
177 )
178 .unwrap(),
179 FileTime::UNIX_EPOCH
180 );
181 assert_eq!(
182 FileTime::from_str_radix(
183 "111111111111111111111111111111111111111111111111111111111111111",
184 2
185 )
186 .unwrap(),
187 FileTime::SIGNED_MAX
188 );
189 assert_eq!(
190 FileTime::from_str_radix(
191 "+111111111111111111111111111111111111111111111111111111111111111",
192 2
193 )
194 .unwrap(),
195 FileTime::SIGNED_MAX
196 );
197 assert_eq!(
198 FileTime::from_str_radix(
199 "1111111111111111111111111111111111111111111111111111111111111111",
200 2
201 )
202 .unwrap(),
203 FileTime::MAX
204 );
205 assert_eq!(
206 FileTime::from_str_radix(
207 "+1111111111111111111111111111111111111111111111111111111111111111",
208 2
209 )
210 .unwrap(),
211 FileTime::MAX
212 );
213 assert_eq!(
214 FileTime::from_str_radix("0", 8).unwrap(),
215 FileTime::NT_TIME_EPOCH
216 );
217 assert_eq!(
218 FileTime::from_str_radix("+0", 8).unwrap(),
219 FileTime::NT_TIME_EPOCH
220 );
221 assert_eq!(
222 FileTime::from_str_radix("6355435732517500000", 8).unwrap(),
223 FileTime::UNIX_EPOCH
224 );
225 assert_eq!(
226 FileTime::from_str_radix("+6355435732517500000", 8).unwrap(),
227 FileTime::UNIX_EPOCH
228 );
229 assert_eq!(
230 FileTime::from_str_radix("777777777777777777777", 8).unwrap(),
231 FileTime::SIGNED_MAX
232 );
233 assert_eq!(
234 FileTime::from_str_radix("+777777777777777777777", 8).unwrap(),
235 FileTime::SIGNED_MAX
236 );
237 assert_eq!(
238 FileTime::from_str_radix("1777777777777777777777", 8).unwrap(),
239 FileTime::MAX
240 );
241 assert_eq!(
242 FileTime::from_str_radix("+1777777777777777777777", 8).unwrap(),
243 FileTime::MAX
244 );
245 assert_eq!(
246 FileTime::from_str_radix("0", 10).unwrap(),
247 FileTime::NT_TIME_EPOCH
248 );
249 assert_eq!(
250 FileTime::from_str_radix("+0", 10).unwrap(),
251 FileTime::NT_TIME_EPOCH
252 );
253 assert_eq!(
254 FileTime::from_str_radix("116444736000000000", 10).unwrap(),
255 FileTime::UNIX_EPOCH
256 );
257 assert_eq!(
258 FileTime::from_str_radix("+116444736000000000", 10).unwrap(),
259 FileTime::UNIX_EPOCH
260 );
261 assert_eq!(
262 FileTime::from_str_radix("9223372036854775807", 10).unwrap(),
263 FileTime::SIGNED_MAX
264 );
265 assert_eq!(
266 FileTime::from_str_radix("+9223372036854775807", 10).unwrap(),
267 FileTime::SIGNED_MAX
268 );
269 assert_eq!(
270 FileTime::from_str_radix("18446744073709551615", 10).unwrap(),
271 FileTime::MAX
272 );
273 assert_eq!(
274 FileTime::from_str_radix("+18446744073709551615", 10).unwrap(),
275 FileTime::MAX
276 );
277 assert_eq!(
278 FileTime::from_str_radix("0", 16).unwrap(),
279 FileTime::NT_TIME_EPOCH
280 );
281 assert_eq!(
282 FileTime::from_str_radix("+0", 16).unwrap(),
283 FileTime::NT_TIME_EPOCH
284 );
285 assert_eq!(
286 FileTime::from_str_radix("19db1ded53e8000", 16).unwrap(),
287 FileTime::UNIX_EPOCH
288 );
289 assert_eq!(
290 FileTime::from_str_radix("19DB1DED53E8000", 16).unwrap(),
291 FileTime::UNIX_EPOCH
292 );
293 assert_eq!(
294 FileTime::from_str_radix("+19db1ded53e8000", 16).unwrap(),
295 FileTime::UNIX_EPOCH
296 );
297 assert_eq!(
298 FileTime::from_str_radix("+19DB1DED53E8000", 16).unwrap(),
299 FileTime::UNIX_EPOCH
300 );
301 assert_eq!(
302 FileTime::from_str_radix("7fffffffffffffff", 16).unwrap(),
303 FileTime::SIGNED_MAX
304 );
305 assert_eq!(
306 FileTime::from_str_radix("7FFFFFFFFFFFFFFF", 16).unwrap(),
307 FileTime::SIGNED_MAX
308 );
309 assert_eq!(
310 FileTime::from_str_radix("+7fffffffffffffff", 16).unwrap(),
311 FileTime::SIGNED_MAX
312 );
313 assert_eq!(
314 FileTime::from_str_radix("+7FFFFFFFFFFFFFFF", 16).unwrap(),
315 FileTime::SIGNED_MAX
316 );
317 assert_eq!(
318 FileTime::from_str_radix("ffffffffffffffff", 16).unwrap(),
319 FileTime::MAX
320 );
321 assert_eq!(
322 FileTime::from_str_radix("FFFFFFFFFFFFFFFF", 16).unwrap(),
323 FileTime::MAX
324 );
325 assert_eq!(
326 FileTime::from_str_radix("+ffffffffffffffff", 16).unwrap(),
327 FileTime::MAX
328 );
329 assert_eq!(
330 FileTime::from_str_radix("+FFFFFFFFFFFFFFFF", 16).unwrap(),
331 FileTime::MAX
332 );
333 assert_eq!(
334 FileTime::from_str_radix("0", 36).unwrap(),
335 FileTime::NT_TIME_EPOCH
336 );
337 assert_eq!(
338 FileTime::from_str_radix("+0", 36).unwrap(),
339 FileTime::NT_TIME_EPOCH
340 );
341 assert_eq!(
342 FileTime::from_str_radix("vuk7p84etc0", 36).unwrap(),
343 FileTime::UNIX_EPOCH
344 );
345 assert_eq!(
346 FileTime::from_str_radix("VUK7P84ETC0", 36).unwrap(),
347 FileTime::UNIX_EPOCH
348 );
349 assert_eq!(
350 FileTime::from_str_radix("+vuk7p84etc0", 36).unwrap(),
351 FileTime::UNIX_EPOCH
352 );
353 assert_eq!(
354 FileTime::from_str_radix("+VUK7P84ETC0", 36).unwrap(),
355 FileTime::UNIX_EPOCH
356 );
357 assert_eq!(
358 FileTime::from_str_radix("1y2p0ij32e8e7", 36).unwrap(),
359 FileTime::SIGNED_MAX
360 );
361 assert_eq!(
362 FileTime::from_str_radix("1Y2P0IJ32E8E7", 36).unwrap(),
363 FileTime::SIGNED_MAX
364 );
365 assert_eq!(
366 FileTime::from_str_radix("+1y2p0ij32e8e7", 36).unwrap(),
367 FileTime::SIGNED_MAX
368 );
369 assert_eq!(
370 FileTime::from_str_radix("+1Y2P0IJ32E8E7", 36).unwrap(),
371 FileTime::SIGNED_MAX
372 );
373 assert_eq!(
374 FileTime::from_str_radix("3w5e11264sgsf", 36).unwrap(),
375 FileTime::MAX
376 );
377 assert_eq!(
378 FileTime::from_str_radix("3W5E11264SGSF", 36).unwrap(),
379 FileTime::MAX
380 );
381 assert_eq!(
382 FileTime::from_str_radix("+3w5e11264sgsf", 36).unwrap(),
383 FileTime::MAX
384 );
385 assert_eq!(
386 FileTime::from_str_radix("+3W5E11264SGSF", 36).unwrap(),
387 FileTime::MAX
388 );
389 }
390
391 #[test]
392 fn from_str_radix_with_invalid_digit_radix() {
393 assert_eq!(
394 FileTime::from_str_radix("2", 2)
395 .unwrap_err()
396 .source()
397 .unwrap()
398 .downcast_ref::<ParseIntError>()
399 .unwrap()
400 .kind(),
401 &IntErrorKind::InvalidDigit
402 );
403 assert_eq!(
404 FileTime::from_str_radix("8", 8)
405 .unwrap_err()
406 .source()
407 .unwrap()
408 .downcast_ref::<ParseIntError>()
409 .unwrap()
410 .kind(),
411 &IntErrorKind::InvalidDigit
412 );
413 assert_eq!(
414 FileTime::from_str_radix("a", 10)
415 .unwrap_err()
416 .source()
417 .unwrap()
418 .downcast_ref::<ParseIntError>()
419 .unwrap()
420 .kind(),
421 &IntErrorKind::InvalidDigit
422 );
423 assert_eq!(
424 FileTime::from_str_radix("A", 10)
425 .unwrap_err()
426 .source()
427 .unwrap()
428 .downcast_ref::<ParseIntError>()
429 .unwrap()
430 .kind(),
431 &IntErrorKind::InvalidDigit
432 );
433 assert_eq!(
434 FileTime::from_str_radix("g", 16)
435 .unwrap_err()
436 .source()
437 .unwrap()
438 .downcast_ref::<ParseIntError>()
439 .unwrap()
440 .kind(),
441 &IntErrorKind::InvalidDigit
442 );
443 assert_eq!(
444 FileTime::from_str_radix("G", 16)
445 .unwrap_err()
446 .source()
447 .unwrap()
448 .downcast_ref::<ParseIntError>()
449 .unwrap()
450 .kind(),
451 &IntErrorKind::InvalidDigit
452 );
453 }
454
455 #[test]
456 fn from_str_radix_when_empty() {
457 assert_eq!(
458 FileTime::from_str_radix("", 16)
459 .unwrap_err()
460 .source()
461 .unwrap()
462 .downcast_ref::<ParseIntError>()
463 .unwrap()
464 .kind(),
465 &IntErrorKind::Empty
466 );
467 }
468
469 #[test]
470 fn from_str_radix_with_invalid_digit() {
471 assert_eq!(
472 FileTime::from_str_radix("Z", 16)
473 .unwrap_err()
474 .source()
475 .unwrap()
476 .downcast_ref::<ParseIntError>()
477 .unwrap()
478 .kind(),
479 &IntErrorKind::InvalidDigit
480 );
481 assert_eq!(
482 FileTime::from_str_radix("_", 16)
483 .unwrap_err()
484 .source()
485 .unwrap()
486 .downcast_ref::<ParseIntError>()
487 .unwrap()
488 .kind(),
489 &IntErrorKind::InvalidDigit
490 );
491 assert_eq!(
492 FileTime::from_str_radix("-1", 16)
493 .unwrap_err()
494 .source()
495 .unwrap()
496 .downcast_ref::<ParseIntError>()
497 .unwrap()
498 .kind(),
499 &IntErrorKind::InvalidDigit
500 );
501 assert_eq!(
502 FileTime::from_str_radix("+", 16)
503 .unwrap_err()
504 .source()
505 .unwrap()
506 .downcast_ref::<ParseIntError>()
507 .unwrap()
508 .kind(),
509 &IntErrorKind::InvalidDigit
510 );
511 assert_eq!(
512 FileTime::from_str_radix("-", 16)
513 .unwrap_err()
514 .source()
515 .unwrap()
516 .downcast_ref::<ParseIntError>()
517 .unwrap()
518 .kind(),
519 &IntErrorKind::InvalidDigit
520 );
521 assert_eq!(
522 FileTime::from_str_radix(" 0", 16)
523 .unwrap_err()
524 .source()
525 .unwrap()
526 .downcast_ref::<ParseIntError>()
527 .unwrap()
528 .kind(),
529 &IntErrorKind::InvalidDigit
530 );
531 assert_eq!(
532 FileTime::from_str_radix("0 ", 16)
533 .unwrap_err()
534 .source()
535 .unwrap()
536 .downcast_ref::<ParseIntError>()
537 .unwrap()
538 .kind(),
539 &IntErrorKind::InvalidDigit
540 );
541 }
542
543 #[test]
544 fn from_str_radix_when_positive_overflow() {
545 assert_eq!(
546 FileTime::from_str_radix(
547 "10000000000000000000000000000000000000000000000000000000000000000",
548 2
549 )
550 .unwrap_err()
551 .source()
552 .unwrap()
553 .downcast_ref::<ParseIntError>()
554 .unwrap()
555 .kind(),
556 &IntErrorKind::PosOverflow
557 );
558 assert_eq!(
559 FileTime::from_str_radix("2000000000000000000000", 8)
560 .unwrap_err()
561 .source()
562 .unwrap()
563 .downcast_ref::<ParseIntError>()
564 .unwrap()
565 .kind(),
566 &IntErrorKind::PosOverflow
567 );
568 assert_eq!(
569 FileTime::from_str_radix("18446744073709551616", 10)
570 .unwrap_err()
571 .source()
572 .unwrap()
573 .downcast_ref::<ParseIntError>()
574 .unwrap()
575 .kind(),
576 &IntErrorKind::PosOverflow
577 );
578 assert_eq!(
579 FileTime::from_str_radix("10000000000000000", 16)
580 .unwrap_err()
581 .source()
582 .unwrap()
583 .downcast_ref::<ParseIntError>()
584 .unwrap()
585 .kind(),
586 &IntErrorKind::PosOverflow
587 );
588 assert_eq!(
589 FileTime::from_str_radix("3w5e11264sgsg", 36)
590 .unwrap_err()
591 .source()
592 .unwrap()
593 .downcast_ref::<ParseIntError>()
594 .unwrap()
595 .kind(),
596 &IntErrorKind::PosOverflow
597 );
598 assert_eq!(
599 FileTime::from_str_radix("3W5E11264SGSG", 36)
600 .unwrap_err()
601 .source()
602 .unwrap()
603 .downcast_ref::<ParseIntError>()
604 .unwrap()
605 .kind(),
606 &IntErrorKind::PosOverflow
607 );
608 }
609
610 #[test]
611 #[should_panic]
612 fn from_str_radix_when_radix_is_less_than_2() {
613 let _ = FileTime::from_str_radix("0", 1);
614 }
615
616 #[test]
617 #[should_panic]
618 fn from_str_radix_when_radix_is_greater_than_36() {
619 let _ = FileTime::from_str_radix("0", 37);
620 }
621
622 #[test]
623 fn from_str() {
624 assert_eq!(FileTime::from_str("0").unwrap(), FileTime::NT_TIME_EPOCH);
625 assert_eq!(FileTime::from_str("+0").unwrap(), FileTime::NT_TIME_EPOCH);
626 assert_eq!(
627 FileTime::from_str("116444736000000000").unwrap(),
628 FileTime::UNIX_EPOCH
629 );
630 assert_eq!(
631 FileTime::from_str("+116444736000000000").unwrap(),
632 FileTime::UNIX_EPOCH
633 );
634 assert_eq!(
635 FileTime::from_str("9223372036854775807").unwrap(),
636 FileTime::SIGNED_MAX
637 );
638 assert_eq!(
639 FileTime::from_str("+9223372036854775807").unwrap(),
640 FileTime::SIGNED_MAX
641 );
642 assert_eq!(
643 FileTime::from_str("18446744073709551615").unwrap(),
644 FileTime::MAX
645 );
646 assert_eq!(
647 FileTime::from_str("+18446744073709551615").unwrap(),
648 FileTime::MAX
649 );
650 }
651
652 #[cfg(feature = "std")]
653 #[test_strategy::proptest]
654 fn from_str_roundtrip(#[strategy(r"\+?[0-9]{1,19}")] s: std::string::String) {
655 use proptest::prop_assert_eq;
656
657 let ft = s.parse().unwrap();
658 prop_assert_eq!(FileTime::from_str(&s).unwrap(), FileTime::new(ft));
659 }
660
661 #[test]
662 fn from_str_when_empty() {
663 assert_eq!(
664 FileTime::from_str("")
665 .unwrap_err()
666 .source()
667 .unwrap()
668 .downcast_ref::<ParseIntError>()
669 .unwrap()
670 .kind(),
671 &IntErrorKind::Empty
672 );
673 }
674
675 #[test]
676 fn from_str_with_invalid_digit() {
677 assert_eq!(
678 FileTime::from_str("a")
679 .unwrap_err()
680 .source()
681 .unwrap()
682 .downcast_ref::<ParseIntError>()
683 .unwrap()
684 .kind(),
685 &IntErrorKind::InvalidDigit
686 );
687 assert_eq!(
688 FileTime::from_str("_")
689 .unwrap_err()
690 .source()
691 .unwrap()
692 .downcast_ref::<ParseIntError>()
693 .unwrap()
694 .kind(),
695 &IntErrorKind::InvalidDigit
696 );
697 assert_eq!(
698 FileTime::from_str("-1")
699 .unwrap_err()
700 .source()
701 .unwrap()
702 .downcast_ref::<ParseIntError>()
703 .unwrap()
704 .kind(),
705 &IntErrorKind::InvalidDigit
706 );
707 assert_eq!(
708 FileTime::from_str("+")
709 .unwrap_err()
710 .source()
711 .unwrap()
712 .downcast_ref::<ParseIntError>()
713 .unwrap()
714 .kind(),
715 &IntErrorKind::InvalidDigit
716 );
717 assert_eq!(
718 FileTime::from_str("-")
719 .unwrap_err()
720 .source()
721 .unwrap()
722 .downcast_ref::<ParseIntError>()
723 .unwrap()
724 .kind(),
725 &IntErrorKind::InvalidDigit
726 );
727 assert_eq!(
728 FileTime::from_str(" 0")
729 .unwrap_err()
730 .source()
731 .unwrap()
732 .downcast_ref::<ParseIntError>()
733 .unwrap()
734 .kind(),
735 &IntErrorKind::InvalidDigit
736 );
737 assert_eq!(
738 FileTime::from_str("0 ")
739 .unwrap_err()
740 .source()
741 .unwrap()
742 .downcast_ref::<ParseIntError>()
743 .unwrap()
744 .kind(),
745 &IntErrorKind::InvalidDigit
746 );
747 }
748
749 #[cfg(feature = "std")]
750 #[test_strategy::proptest]
751 fn from_str_with_invalid_digit_roundtrip(
752 #[strategy(r"-[0-9]+|[^0-9]+")] s: std::string::String,
753 ) {
754 use proptest::prop_assert;
755
756 prop_assert!(FileTime::from_str(&s).is_err());
757 }
758
759 #[test]
760 fn from_str_when_positive_overflow() {
761 assert_eq!(
762 FileTime::from_str("18446744073709551616")
763 .unwrap_err()
764 .source()
765 .unwrap()
766 .downcast_ref::<ParseIntError>()
767 .unwrap()
768 .kind(),
769 &IntErrorKind::PosOverflow
770 );
771 }
772}