use alloc::boxed::Box;
use quickcheck_dep::{empty_shrinker, single_shrinker, Arbitrary, Gen};
use crate::date::{MAX_YEAR, MIN_YEAR};
use crate::util::days_in_year;
use crate::{Date, Duration, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset, Weekday};
macro_rules! arbitrary_between {
($type:ty; $gen:expr, $min:expr, $max:expr) => {{
let min = $min;
let max = $max;
let range = max - min;
<$type>::arbitrary($gen).rem_euclid(range + 1) + min
}};
}
impl Arbitrary for Date {
fn arbitrary(g: &mut Gen) -> Self {
let year = arbitrary_between!(i32; g, MIN_YEAR, MAX_YEAR);
let ordinal = arbitrary_between!(u16; g, 1, days_in_year(year));
Self::__from_ordinal_date_unchecked(year, ordinal)
}
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new(
self.to_ordinal_date()
.shrink()
.flat_map(|(year, ordinal)| Self::from_ordinal_date(year, ordinal)),
)
}
}
impl Arbitrary for Duration {
fn arbitrary(g: &mut Gen) -> Self {
let seconds = i64::arbitrary(g);
let mut nanoseconds = arbitrary_between!(i32; g, 0, 999_999_999);
if seconds < 0 || (seconds == 0 && bool::arbitrary(g)) {
nanoseconds *= -1;
}
Self::new_unchecked(seconds, nanoseconds)
}
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new(
(self.subsec_nanoseconds(), self.whole_seconds())
.shrink()
.map(|(mut nanoseconds, seconds)| {
if (seconds > 0 && nanoseconds < 0) || (seconds < 0 && nanoseconds > 0) {
nanoseconds *= -1;
}
Self::new_unchecked(seconds, nanoseconds)
}),
)
}
}
impl Arbitrary for Time {
fn arbitrary(g: &mut Gen) -> Self {
Self::__from_hms_nanos_unchecked(
arbitrary_between!(u8; g, 0, 23),
arbitrary_between!(u8; g, 0, 59),
arbitrary_between!(u8; g, 0, 59),
arbitrary_between!(u32; g, 0, 999_999_999),
)
}
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new(
self.as_hms_nano()
.shrink()
.map(|(hour, minute, second, nanosecond)| {
Self::__from_hms_nanos_unchecked(hour, minute, second, nanosecond)
}),
)
}
}
impl Arbitrary for PrimitiveDateTime {
fn arbitrary(g: &mut Gen) -> Self {
Self::new(Date::arbitrary(g), Time::arbitrary(g))
}
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new(
(self.date, self.time)
.shrink()
.map(|(date, time)| Self { date, time }),
)
}
}
impl Arbitrary for UtcOffset {
fn arbitrary(g: &mut Gen) -> Self {
let hours = arbitrary_between!(i8; g, -23, 23);
let mut minutes = arbitrary_between!(i8; g, 0, 59);
let mut seconds = arbitrary_between!(i8; g, 0, 59);
if hours < 0
|| (hours == 0 && bool::arbitrary(g))
|| (hours == 0 && minutes == 0 && bool::arbitrary(g))
{
minutes *= -1;
seconds *= -1;
}
Self::__from_hms_unchecked(hours, minutes, seconds)
}
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new(
self.as_hms().shrink().map(|(hours, minutes, seconds)| {
Self::__from_hms_unchecked(hours, minutes, seconds)
}),
)
}
}
impl Arbitrary for OffsetDateTime {
fn arbitrary(g: &mut Gen) -> Self {
let datetime = PrimitiveDateTime::arbitrary(g);
let offset = UtcOffset::arbitrary(g);
datetime.assume_offset(offset)
}
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new(
(self.utc_datetime.utc_to_offset(self.offset), self.offset)
.shrink()
.map(|(utc_datetime, offset)| utc_datetime.assume_offset(offset)),
)
}
}
impl Arbitrary for Weekday {
fn arbitrary(g: &mut Gen) -> Self {
use Weekday::*;
match arbitrary_between!(u8; g, 0, 6) {
0 => Monday,
1 => Tuesday,
2 => Wednesday,
3 => Thursday,
4 => Friday,
5 => Saturday,
_ => Sunday,
}
}
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
match self {
Self::Monday => empty_shrinker(),
_ => single_shrinker(self.previous()),
}
}
}