1use core::str::FromStr;
8
9use super::FileTime;
10use crate::error::ParseFileTimeError;
11
12impl FileTime {
13 pub const fn from_str_radix(src: &str, radix: u32) -> Result<Self, ParseFileTimeError> {
85 match u64::from_str_radix(src, radix) {
86 Ok(ft) => Ok(Self::new(ft)),
87 Err(err) => Err(ParseFileTimeError::new(err)),
88 }
89 }
90}
91
92impl FromStr for FileTime {
93 type Err = ParseFileTimeError;
94
95 fn from_str(src: &str) -> Result<Self, Self::Err> {
141 Self::from_str_radix(src, 10)
142 }
143}
144
145#[cfg(test)]
146mod tests {
147 use core::{
148 error::Error,
149 num::{IntErrorKind, ParseIntError},
150 };
151
152 #[cfg(feature = "std")]
153 use proptest::{prop_assert, prop_assert_eq};
154 #[cfg(feature = "std")]
155 use test_strategy::proptest;
156
157 use super::*;
158
159 #[test]
160 fn from_str_radix() {
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("+0", 2).unwrap(),
167 FileTime::NT_TIME_EPOCH
168 );
169 assert_eq!(
170 FileTime::from_str_radix(
171 "110011101101100011101111011010101001111101000000000000000",
172 2
173 )
174 .unwrap(),
175 FileTime::UNIX_EPOCH
176 );
177 assert_eq!(
178 FileTime::from_str_radix(
179 "+110011101101100011101111011010101001111101000000000000000",
180 2
181 )
182 .unwrap(),
183 FileTime::UNIX_EPOCH
184 );
185 assert_eq!(
186 FileTime::from_str_radix(
187 "111111111111111111111111111111111111111111111111111111111111111",
188 2
189 )
190 .unwrap(),
191 FileTime::SIGNED_MAX
192 );
193 assert_eq!(
194 FileTime::from_str_radix(
195 "+111111111111111111111111111111111111111111111111111111111111111",
196 2
197 )
198 .unwrap(),
199 FileTime::SIGNED_MAX
200 );
201 assert_eq!(
202 FileTime::from_str_radix(
203 "1111111111111111111111111111111111111111111111111111111111111111",
204 2
205 )
206 .unwrap(),
207 FileTime::MAX
208 );
209 assert_eq!(
210 FileTime::from_str_radix(
211 "+1111111111111111111111111111111111111111111111111111111111111111",
212 2
213 )
214 .unwrap(),
215 FileTime::MAX
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("+0", 8).unwrap(),
223 FileTime::NT_TIME_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("+6355435732517500000", 8).unwrap(),
231 FileTime::UNIX_EPOCH
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("+777777777777777777777", 8).unwrap(),
239 FileTime::SIGNED_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("+1777777777777777777777", 8).unwrap(),
247 FileTime::MAX
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("+0", 10).unwrap(),
255 FileTime::NT_TIME_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("+116444736000000000", 10).unwrap(),
263 FileTime::UNIX_EPOCH
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("+9223372036854775807", 10).unwrap(),
271 FileTime::SIGNED_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("+18446744073709551615", 10).unwrap(),
279 FileTime::MAX
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("+0", 16).unwrap(),
287 FileTime::NT_TIME_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("+19DB1DED53E8000", 16).unwrap(),
303 FileTime::UNIX_EPOCH
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("+7FFFFFFFFFFFFFFF", 16).unwrap(),
319 FileTime::SIGNED_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("+FFFFFFFFFFFFFFFF", 16).unwrap(),
335 FileTime::MAX
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("+0", 36).unwrap(),
343 FileTime::NT_TIME_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("+VUK7P84ETC0", 36).unwrap(),
359 FileTime::UNIX_EPOCH
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("+1Y2P0IJ32E8E7", 36).unwrap(),
375 FileTime::SIGNED_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 assert_eq!(
390 FileTime::from_str_radix("+3W5E11264SGSF", 36).unwrap(),
391 FileTime::MAX
392 );
393 }
394
395 #[test]
396 fn from_str_radix_with_invalid_digit_radix() {
397 assert_eq!(
398 FileTime::from_str_radix("2", 2)
399 .unwrap_err()
400 .source()
401 .unwrap()
402 .downcast_ref::<ParseIntError>()
403 .unwrap()
404 .kind(),
405 &IntErrorKind::InvalidDigit
406 );
407 assert_eq!(
408 FileTime::from_str_radix("8", 8)
409 .unwrap_err()
410 .source()
411 .unwrap()
412 .downcast_ref::<ParseIntError>()
413 .unwrap()
414 .kind(),
415 &IntErrorKind::InvalidDigit
416 );
417 assert_eq!(
418 FileTime::from_str_radix("a", 10)
419 .unwrap_err()
420 .source()
421 .unwrap()
422 .downcast_ref::<ParseIntError>()
423 .unwrap()
424 .kind(),
425 &IntErrorKind::InvalidDigit
426 );
427 assert_eq!(
428 FileTime::from_str_radix("A", 10)
429 .unwrap_err()
430 .source()
431 .unwrap()
432 .downcast_ref::<ParseIntError>()
433 .unwrap()
434 .kind(),
435 &IntErrorKind::InvalidDigit
436 );
437 assert_eq!(
438 FileTime::from_str_radix("g", 16)
439 .unwrap_err()
440 .source()
441 .unwrap()
442 .downcast_ref::<ParseIntError>()
443 .unwrap()
444 .kind(),
445 &IntErrorKind::InvalidDigit
446 );
447 assert_eq!(
448 FileTime::from_str_radix("G", 16)
449 .unwrap_err()
450 .source()
451 .unwrap()
452 .downcast_ref::<ParseIntError>()
453 .unwrap()
454 .kind(),
455 &IntErrorKind::InvalidDigit
456 );
457 }
458
459 #[test]
460 fn from_str_radix_when_empty() {
461 assert_eq!(
462 FileTime::from_str_radix("", 16)
463 .unwrap_err()
464 .source()
465 .unwrap()
466 .downcast_ref::<ParseIntError>()
467 .unwrap()
468 .kind(),
469 &IntErrorKind::Empty
470 );
471 }
472
473 #[test]
474 fn from_str_radix_with_invalid_digit() {
475 assert_eq!(
476 FileTime::from_str_radix("Z", 16)
477 .unwrap_err()
478 .source()
479 .unwrap()
480 .downcast_ref::<ParseIntError>()
481 .unwrap()
482 .kind(),
483 &IntErrorKind::InvalidDigit
484 );
485 assert_eq!(
486 FileTime::from_str_radix("_", 16)
487 .unwrap_err()
488 .source()
489 .unwrap()
490 .downcast_ref::<ParseIntError>()
491 .unwrap()
492 .kind(),
493 &IntErrorKind::InvalidDigit
494 );
495 assert_eq!(
496 FileTime::from_str_radix("-1", 16)
497 .unwrap_err()
498 .source()
499 .unwrap()
500 .downcast_ref::<ParseIntError>()
501 .unwrap()
502 .kind(),
503 &IntErrorKind::InvalidDigit
504 );
505 assert_eq!(
506 FileTime::from_str_radix("+", 16)
507 .unwrap_err()
508 .source()
509 .unwrap()
510 .downcast_ref::<ParseIntError>()
511 .unwrap()
512 .kind(),
513 &IntErrorKind::InvalidDigit
514 );
515 assert_eq!(
516 FileTime::from_str_radix("-", 16)
517 .unwrap_err()
518 .source()
519 .unwrap()
520 .downcast_ref::<ParseIntError>()
521 .unwrap()
522 .kind(),
523 &IntErrorKind::InvalidDigit
524 );
525 assert_eq!(
526 FileTime::from_str_radix(" 0", 16)
527 .unwrap_err()
528 .source()
529 .unwrap()
530 .downcast_ref::<ParseIntError>()
531 .unwrap()
532 .kind(),
533 &IntErrorKind::InvalidDigit
534 );
535 assert_eq!(
536 FileTime::from_str_radix("0 ", 16)
537 .unwrap_err()
538 .source()
539 .unwrap()
540 .downcast_ref::<ParseIntError>()
541 .unwrap()
542 .kind(),
543 &IntErrorKind::InvalidDigit
544 );
545 }
546
547 #[test]
548 fn from_str_radix_when_positive_overflow() {
549 assert_eq!(
550 FileTime::from_str_radix(
551 "10000000000000000000000000000000000000000000000000000000000000000",
552 2
553 )
554 .unwrap_err()
555 .source()
556 .unwrap()
557 .downcast_ref::<ParseIntError>()
558 .unwrap()
559 .kind(),
560 &IntErrorKind::PosOverflow
561 );
562 assert_eq!(
563 FileTime::from_str_radix("2000000000000000000000", 8)
564 .unwrap_err()
565 .source()
566 .unwrap()
567 .downcast_ref::<ParseIntError>()
568 .unwrap()
569 .kind(),
570 &IntErrorKind::PosOverflow
571 );
572 assert_eq!(
573 FileTime::from_str_radix("18446744073709551616", 10)
574 .unwrap_err()
575 .source()
576 .unwrap()
577 .downcast_ref::<ParseIntError>()
578 .unwrap()
579 .kind(),
580 &IntErrorKind::PosOverflow
581 );
582 assert_eq!(
583 FileTime::from_str_radix("10000000000000000", 16)
584 .unwrap_err()
585 .source()
586 .unwrap()
587 .downcast_ref::<ParseIntError>()
588 .unwrap()
589 .kind(),
590 &IntErrorKind::PosOverflow
591 );
592 assert_eq!(
593 FileTime::from_str_radix("3w5e11264sgsg", 36)
594 .unwrap_err()
595 .source()
596 .unwrap()
597 .downcast_ref::<ParseIntError>()
598 .unwrap()
599 .kind(),
600 &IntErrorKind::PosOverflow
601 );
602 assert_eq!(
603 FileTime::from_str_radix("3W5E11264SGSG", 36)
604 .unwrap_err()
605 .source()
606 .unwrap()
607 .downcast_ref::<ParseIntError>()
608 .unwrap()
609 .kind(),
610 &IntErrorKind::PosOverflow
611 );
612 }
613
614 #[test]
615 #[should_panic]
616 fn from_str_radix_when_radix_is_less_than_2() {
617 let _ = FileTime::from_str_radix("0", 1);
618 }
619
620 #[test]
621 #[should_panic]
622 fn from_str_radix_when_radix_is_greater_than_36() {
623 let _ = FileTime::from_str_radix("0", 37);
624 }
625
626 #[test]
627 const fn from_str_radix_is_const_fn() {
628 const _: Result<FileTime, ParseFileTimeError> = FileTime::from_str_radix("0", 2);
629 }
630
631 #[test]
632 fn from_str() {
633 assert_eq!(FileTime::from_str("0").unwrap(), FileTime::NT_TIME_EPOCH);
634 assert_eq!(FileTime::from_str("+0").unwrap(), FileTime::NT_TIME_EPOCH);
635 assert_eq!(
636 FileTime::from_str("116444736000000000").unwrap(),
637 FileTime::UNIX_EPOCH
638 );
639 assert_eq!(
640 FileTime::from_str("+116444736000000000").unwrap(),
641 FileTime::UNIX_EPOCH
642 );
643 assert_eq!(
644 FileTime::from_str("9223372036854775807").unwrap(),
645 FileTime::SIGNED_MAX
646 );
647 assert_eq!(
648 FileTime::from_str("+9223372036854775807").unwrap(),
649 FileTime::SIGNED_MAX
650 );
651 assert_eq!(
652 FileTime::from_str("18446744073709551615").unwrap(),
653 FileTime::MAX
654 );
655 assert_eq!(
656 FileTime::from_str("+18446744073709551615").unwrap(),
657 FileTime::MAX
658 );
659 }
660
661 #[cfg(feature = "std")]
662 #[proptest]
663 fn from_str_roundtrip(#[strategy(r"\+?[0-9]{1,19}")] s: std::string::String) {
664 let ft = s.parse().unwrap();
665 prop_assert_eq!(FileTime::from_str(&s).unwrap(), FileTime::new(ft));
666 }
667
668 #[test]
669 fn from_str_when_empty() {
670 assert_eq!(
671 FileTime::from_str("")
672 .unwrap_err()
673 .source()
674 .unwrap()
675 .downcast_ref::<ParseIntError>()
676 .unwrap()
677 .kind(),
678 &IntErrorKind::Empty
679 );
680 }
681
682 #[test]
683 fn from_str_with_invalid_digit() {
684 assert_eq!(
685 FileTime::from_str("a")
686 .unwrap_err()
687 .source()
688 .unwrap()
689 .downcast_ref::<ParseIntError>()
690 .unwrap()
691 .kind(),
692 &IntErrorKind::InvalidDigit
693 );
694 assert_eq!(
695 FileTime::from_str("_")
696 .unwrap_err()
697 .source()
698 .unwrap()
699 .downcast_ref::<ParseIntError>()
700 .unwrap()
701 .kind(),
702 &IntErrorKind::InvalidDigit
703 );
704 assert_eq!(
705 FileTime::from_str("-1")
706 .unwrap_err()
707 .source()
708 .unwrap()
709 .downcast_ref::<ParseIntError>()
710 .unwrap()
711 .kind(),
712 &IntErrorKind::InvalidDigit
713 );
714 assert_eq!(
715 FileTime::from_str("+")
716 .unwrap_err()
717 .source()
718 .unwrap()
719 .downcast_ref::<ParseIntError>()
720 .unwrap()
721 .kind(),
722 &IntErrorKind::InvalidDigit
723 );
724 assert_eq!(
725 FileTime::from_str("-")
726 .unwrap_err()
727 .source()
728 .unwrap()
729 .downcast_ref::<ParseIntError>()
730 .unwrap()
731 .kind(),
732 &IntErrorKind::InvalidDigit
733 );
734 assert_eq!(
735 FileTime::from_str(" 0")
736 .unwrap_err()
737 .source()
738 .unwrap()
739 .downcast_ref::<ParseIntError>()
740 .unwrap()
741 .kind(),
742 &IntErrorKind::InvalidDigit
743 );
744 assert_eq!(
745 FileTime::from_str("0 ")
746 .unwrap_err()
747 .source()
748 .unwrap()
749 .downcast_ref::<ParseIntError>()
750 .unwrap()
751 .kind(),
752 &IntErrorKind::InvalidDigit
753 );
754 }
755
756 #[cfg(feature = "std")]
757 #[proptest]
758 fn from_str_with_invalid_digit_roundtrip(
759 #[strategy(r"-[0-9]+|[^0-9]+")] s: std::string::String,
760 ) {
761 prop_assert!(FileTime::from_str(&s).is_err());
762 }
763
764 #[test]
765 fn from_str_when_positive_overflow() {
766 assert_eq!(
767 FileTime::from_str("18446744073709551616")
768 .unwrap_err()
769 .source()
770 .unwrap()
771 .downcast_ref::<ParseIntError>()
772 .unwrap()
773 .kind(),
774 &IntErrorKind::PosOverflow
775 );
776 }
777}