use crate::constants as c;
macro_rules! define_bounds {
($(
$(#[$attr:meta])*
(
// The name of the boundary type.
$name:ident,
// The underlying primitive type. This is usually, but not always,
// the smallest signed primitive integer type that can represent
// both the minimum and maximum boundary values.
$ty:ident,
// A short human readable description that appears in error
// messages when the boundaries of this type are violated.
$what:expr,
// The minimum value.
$min:expr,
// The maximum value.
$max:expr $(,)?
)
),* $(,)?) => {
$(
$(#[$attr])*
#[allow(missing_debug_implementations)]
#[allow(missing_docs)]
#[derive(Eq, PartialEq)]
pub struct $name(());
impl Bounds for $name {
const WHAT: &'static str = $what;
const MIN: Self::Primitive = $min;
const MAX: Self::Primitive = $max;
type Primitive = $ty;
type Error = BoundsError;
#[cold]
#[inline(never)]
fn error() -> BoundsError {
Self::error()
}
}
#[allow(dead_code)]
#[allow(missing_docs)]
impl $name {
pub const MIN: $ty = <$name as Bounds>::MIN;
pub const MAX: $ty = <$name as Bounds>::MAX;
pub const LEN: i128 = Self::MAX as i128 - Self::MIN as i128 + 1;
#[cold]
pub const fn error() -> BoundsError {
BoundsError {
kind: BoundsErrorKind::$name(RawBoundsError::new()),
}
}
#[inline(always)]
pub fn check(n: impl Into<i64>) -> Result<$ty, BoundsError> {
<$name as Bounds>::check(n)
}
#[inline(always)]
pub const fn checkc(n: i64) -> Result<$ty, BoundsError> {
match self::const_check::$ty(n) {
Ok(n) => Ok(n),
Err(err) => Err(BoundsError {
kind: BoundsErrorKind::$name(err),
}),
}
}
#[inline(always)]
pub const fn checked_add(n1: $ty, n2: $ty) -> Result<$ty, BoundsError> {
match self::const_checked_add::$ty(n1, n2) {
Ok(n) => Ok(n),
Err(err) => Err(BoundsError {
kind: BoundsErrorKind::$name(err),
}),
}
}
#[inline(always)]
pub fn checked_mul(n1: $ty, n2: $ty) -> Result<$ty, BoundsError> {
<$name as Bounds>::checked_mul(n1, n2)
}
#[cfg(test)]
pub(crate) fn arbitrary(g: &mut quickcheck::Gen) -> $ty {
use quickcheck::Arbitrary;
let mut n: $ty = <$ty>::arbitrary(g);
n = n.wrapping_rem_euclid(Self::LEN as $ty);
n += Self::MIN;
n
}
}
)*
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
enum BoundsErrorKind {
$($name(RawBoundsError<$name>),)*
}
impl core::fmt::Display for BoundsErrorKind {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
match *self {
$(BoundsErrorKind::$name(ref err) => err.fmt(f),)*
}
}
}
}
}
define_bounds! {
(
CivilDayNanosecond,
i64,
"nanoseconds (in one civil day)",
0,
c::NANOS_PER_CIVIL_DAY - 1,
),
(
CivilDaySecond,
i32,
"seconds (in one civil day)",
0,
c::SECS_PER_CIVIL_DAY_32 - 1,
),
(Day, i8, "day", 1, 31),
(DayOfYear, i16, "day-of-year", 1, 366),
(DayOfYearNoLeap, i16, "day-of-year (skipping Feb 29)", 1, 365),
(
DeltaSeconds,
i64,
"seconds",
-Self::MAX,
next_multiple_of(
UnixEpochSeconds::LEN as i64
+ OffsetTotalSeconds::MAX as i64
+ c::SECS_PER_CIVIL_DAY,
c::SECS_PER_CIVIL_DAY,
),
),
(Hour, i8, "hour", 0, 23),
(ISOWeek, i8, "iso-week", 1, 53),
(ISOYear, i16, "iso-year", Year::MIN, Year::MAX),
(Microsecond, i16, "microsecond", 0, 999),
(Millisecond, i16, "millisecond", 0, 999),
(Minute, i8, "minute", 0, 59),
(Month, i8, "month", 1, 12),
(Nanosecond, i16, "nanosecond", 0, 999),
(
NthWeekday,
i32,
"nth weekday",
-Self::MAX,
(DeltaSeconds::MAX / c::SECS_PER_CIVIL_WEEK) as i32,
),
(NthWeekdayOfMonth, i8, "nth weekday of month", -5, 5),
(OffsetHours, i8, "time zone offset hours", -25, 25),
(OffsetMinutes, i8, "time zone offset minutes", -59, 59),
(OffsetSeconds, i8, "time zone offset seconds", -59, 59),
(
OffsetTotalSeconds,
i32,
"time zone offset total seconds",
-Self::MAX,
(OffsetHours::MAX as i32 * c::SECS_PER_HOUR_32)
+ (OffsetMinutes::MAX as i32 * c::MINS_PER_HOUR_32)
+ OffsetSeconds::MAX as i32,
),
(Second, i8, "second", 0, 59),
(SubsecNanosecond, i32, "subsecond nanosecond", 0, c::NANOS_PER_SEC_32 - 1),
(
SignedSubsecNanosecond,
i32,
"subsecond nanosecond",
-SubsecNanosecond::MAX,
SubsecNanosecond::MAX,
),
(TimestampArithmeticNanosecond, i32, "nanosecond", i32::MIN, i32::MAX),
(
UnixEpochDays,
i32,
"Unix epoch days",
(UnixEpochSeconds::MIN + OffsetTotalSeconds::MIN as i64).div_euclid(c::SECS_PER_CIVIL_DAY) as i32,
(UnixEpochSeconds::MAX + OffsetTotalSeconds::MAX as i64).div_euclid(c::SECS_PER_CIVIL_DAY) as i32,
),
(
UnixEpochMilliseconds,
i64,
"Unix timestamp milliseconds",
UnixEpochSeconds::MIN * c::MILLIS_PER_SEC,
UnixEpochSeconds::MAX * c::MILLIS_PER_SEC,
),
(
UnixEpochMicroseconds,
i64,
"Unix timestamp microseconds",
UnixEpochMilliseconds::MIN * c::MICROS_PER_MILLI,
UnixEpochMilliseconds::MAX * c::MICROS_PER_MILLI,
),
(
UnixEpochSeconds,
i64,
"Unix timestamp seconds",
-377705116800 - OffsetTotalSeconds::MIN as i64,
253402300799 - OffsetTotalSeconds::MAX as i64,
),
(WeekdayMondayZero, i8, "weekday (Monday 0-indexed)", 0, 6),
(WeekdayMondayOne, i8, "weekday (Monday 1-indexed)", 1, 7),
(WeekdaySundayZero, i8, "weekday (Sunday 0-indexed)", 0, 6),
(WeekdaySundayOne, i8, "weekday (Sunday 1-indexed)", 1, 7),
(Year, i16, "year", -9999, 9999),
(YearCE, i16, "CE year", 1, Year::MAX),
(YearBCE, i16, "BCE year", 1, Year::MAX + 1),
}
#[allow(missing_docs)]
pub trait Primitive:
Clone
+ Copy
+ Eq
+ PartialEq
+ PartialOrd
+ Ord
+ core::fmt::Debug
+ core::fmt::Display
{
fn as_i8(self) -> i8;
fn as_i16(self) -> i16;
fn as_i32(self) -> i32;
fn as_i64(self) -> i64;
fn from_i8(n: i8) -> Self;
fn from_i16(n: i16) -> Self;
fn from_i32(n: i32) -> Self;
fn from_i64(n: i64) -> Self;
fn checked_add(self, n: Self) -> Option<Self>;
fn checked_mul(self, n: Self) -> Option<Self>;
}
macro_rules! impl_primitive {
($($intty:ty),*) => {
$(
impl Primitive for $intty {
fn as_i8(self) -> i8 {
#[cfg(debug_assertions)]
{
i8::try_from(self).unwrap()
}
#[cfg(not(debug_assertions))]
{
self as i8
}
}
fn as_i16(self) -> i16 {
#[cfg(debug_assertions)]
{
i16::try_from(self).unwrap()
}
#[cfg(not(debug_assertions))]
{
self as i16
}
}
fn as_i32(self) -> i32 {
#[cfg(debug_assertions)]
{
i32::try_from(self).unwrap()
}
#[cfg(not(debug_assertions))]
{
self as i32
}
}
fn as_i64(self) -> i64 {
#[cfg(debug_assertions)]
{
i64::try_from(self).unwrap()
}
#[cfg(not(debug_assertions))]
{
self as i64
}
}
fn from_i8(n: i8) -> Self {
#[cfg(debug_assertions)]
{
Self::try_from(n).unwrap()
}
#[cfg(not(debug_assertions))]
{
n as Self
}
}
fn from_i16(n: i16) -> Self {
#[cfg(debug_assertions)]
{
Self::try_from(n).unwrap()
}
#[cfg(not(debug_assertions))]
{
n as Self
}
}
fn from_i32(n: i32) -> Self {
#[cfg(debug_assertions)]
{
Self::try_from(n).unwrap()
}
#[cfg(not(debug_assertions))]
{
n as Self
}
}
fn from_i64(n: i64) -> Self {
#[cfg(debug_assertions)]
{
Self::try_from(n).unwrap()
}
#[cfg(not(debug_assertions))]
{
n as Self
}
}
fn checked_add(self, n: $intty) -> Option<$intty> {
<$intty>::checked_add(self, n)
}
fn checked_mul(self, n: $intty) -> Option<$intty> {
<$intty>::checked_mul(self, n)
}
}
)*
}
}
impl_primitive!(i8, i16, i32, i64);
pub trait Bounds: Sized {
const WHAT: &'static str;
const MIN: Self::Primitive;
const MAX: Self::Primitive;
type Primitive: Primitive;
type Error;
fn error() -> Self::Error;
#[inline(always)]
fn check(n: impl Into<i64>) -> Result<Self::Primitive, Self::Error> {
let n = n.into();
if !(Self::MIN.as_i64() <= n && n <= Self::MAX.as_i64()) {
return Err(Self::error());
}
Ok(Self::Primitive::from_i64(n))
}
#[inline(always)]
fn check_self(n: Self::Primitive) -> Result<Self::Primitive, Self::Error> {
if !(Self::MIN <= n && n <= Self::MAX) {
return Err(Self::error());
}
Ok(n)
}
#[inline(always)]
fn checked_add(
n1: Self::Primitive,
n2: Self::Primitive,
) -> Result<Self::Primitive, Self::Error> {
Self::check_self(n1.checked_add(n2).ok_or_else(Self::error)?)
}
#[inline(always)]
fn checked_mul(
n1: Self::Primitive,
n2: Self::Primitive,
) -> Result<Self::Primitive, Self::Error> {
Self::check_self(n1.checked_mul(n2).ok_or_else(Self::error)?)
}
}
#[derive(Eq, PartialEq)]
pub struct RawBoundsError<B>(core::marker::PhantomData<B>);
impl<B> RawBoundsError<B> {
#[inline]
pub const fn new() -> RawBoundsError<B> {
RawBoundsError(core::marker::PhantomData)
}
}
impl<B> Copy for RawBoundsError<B> {}
impl<B> Clone for RawBoundsError<B> {
#[inline]
fn clone(&self) -> RawBoundsError<B> {
RawBoundsError::new()
}
}
impl<B, P> core::fmt::Debug for RawBoundsError<B>
where
B: Bounds<Primitive = P>,
P: core::fmt::Debug,
{
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("RawBoundsError")
.field("what", &B::WHAT)
.field("min", &B::MIN)
.field("max", &B::MAX)
.finish()
}
}
impl<B, P> core::fmt::Display for RawBoundsError<B>
where
B: Bounds<Primitive = P>,
P: core::fmt::Display,
{
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(
f,
"parameter '{what}' is not in the required range of {min}..={max}",
what = B::WHAT,
min = B::MIN,
max = B::MAX,
)
}
}
#[cfg(feature = "defmt")]
impl<B, P> defmt::Format for RawBoundsError<B>
where
B: Bounds<Primitive = P>,
P: defmt::Format,
{
fn format(&self, f: defmt::Formatter) {
defmt::write!(
f,
"RawBoundsError {{ what: {=str}, min: {}, max: {} }}",
B::WHAT,
B::MIN,
B::MAX
);
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct BoundsError {
kind: BoundsErrorKind,
}
impl BoundsError {
pub(crate) const fn into_range_error(self) -> RangeError {
RangeError { kind: RangeErrorKind::Bounds(self) }
}
}
impl core::fmt::Display for BoundsError {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
self.kind.fmt(f)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct RangeError {
kind: RangeErrorKind,
}
impl From<BoundsError> for RangeError {
fn from(err: BoundsError) -> RangeError {
err.into_range_error()
}
}
impl From<SpecialBoundsError> for RangeError {
fn from(err: SpecialBoundsError) -> RangeError {
err.into_range_error()
}
}
impl core::fmt::Display for RangeError {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
match self.kind {
RangeErrorKind::Bounds(ref err) => err.fmt(f),
RangeErrorKind::Special(ref err) => err.fmt(f),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for RangeError {}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
enum RangeErrorKind {
Bounds(BoundsError),
Special(SpecialBoundsError),
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub(crate) enum SpecialBoundsError {
DateInvalidDay { year: i16, month: i8 },
DateInvalidDayOfYear { year: i16 },
UnixEpochNanoseconds,
}
impl SpecialBoundsError {
pub(crate) const fn into_range_error(self) -> RangeError {
RangeError { kind: RangeErrorKind::Special(self) }
}
}
impl core::fmt::Display for SpecialBoundsError {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
use self::SpecialBoundsError::*;
match *self {
DateInvalidDay { year, month } => write!(
f,
"parameter 'day' for `{year:04}-{month:02}` is invalid, \
must be in range `1..={max_day}`",
max_day = crate::civil::days_in_month(year, month),
),
DateInvalidDayOfYear { year } => write!(
f,
"number of days for `{year:04}` is invalid, \
must be in range `1..={max_day}`",
max_day = crate::civil::days_in_year(year),
),
UnixEpochNanoseconds => write!(
f,
"parameter 'Unix timestamp nanoseconds' \
is not in the required range of {min}..={max}",
min = UnixEpochMicroseconds::MIN as i128
* (c::NANOS_PER_MICRO as i128),
max = UnixEpochMicroseconds::MAX as i128
* (c::NANOS_PER_MICRO as i128),
),
}
}
}
#[allow(missing_docs)]
pub mod const_check {
use super::{Bounds, RawBoundsError};
#[inline(always)]
pub const fn i8<B>(n: i64) -> Result<i8, RawBoundsError<B>>
where
B: Bounds<Primitive = i8>,
{
if !((B::MIN as i64) <= n && n <= (B::MAX as i64)) {
return Err(RawBoundsError::new());
}
Ok(n as i8)
}
#[inline(always)]
pub const fn i16<B>(n: i64) -> Result<i16, RawBoundsError<B>>
where
B: Bounds<Primitive = i16>,
{
if !((B::MIN as i64) <= n && n <= (B::MAX as i64)) {
return Err(RawBoundsError::new());
}
Ok(n as i16)
}
#[inline(always)]
pub const fn i32<B>(n: i64) -> Result<i32, RawBoundsError<B>>
where
B: Bounds<Primitive = i32>,
{
if !((B::MIN as i64) <= n && n <= (B::MAX as i64)) {
return Err(RawBoundsError::new());
}
Ok(n as i32)
}
#[inline(always)]
pub const fn i64<B>(n: i64) -> Result<i64, RawBoundsError<B>>
where
B: Bounds<Primitive = i64>,
{
if !(B::MIN <= n && n <= B::MAX) {
return Err(RawBoundsError::new());
}
Ok(n)
}
}
#[allow(missing_docs)]
pub mod const_checked_add {
use super::{Bounds, RawBoundsError};
#[inline(always)]
pub const fn i8<B>(n1: i8, n2: i8) -> Result<i8, RawBoundsError<B>>
where
B: Bounds<Primitive = i8>,
{
let sum = match n1.checked_add(n2) {
Some(sum) => sum,
None => return Err(RawBoundsError::new()),
};
super::const_check::i8(sum as i64)
}
#[inline(always)]
pub const fn i16<B>(n1: i16, n2: i16) -> Result<i16, RawBoundsError<B>>
where
B: Bounds<Primitive = i16>,
{
let sum = match n1.checked_add(n2) {
Some(sum) => sum,
None => return Err(RawBoundsError::new()),
};
super::const_check::i16(sum as i64)
}
#[inline(always)]
pub const fn i32<B>(n1: i32, n2: i32) -> Result<i32, RawBoundsError<B>>
where
B: Bounds<Primitive = i32>,
{
let sum = match n1.checked_add(n2) {
Some(sum) => sum,
None => return Err(RawBoundsError::new()),
};
super::const_check::i32(sum as i64)
}
#[inline(always)]
pub const fn i64<B>(n1: i64, n2: i64) -> Result<i64, RawBoundsError<B>>
where
B: Bounds<Primitive = i64>,
{
let sum = match n1.checked_add(n2) {
Some(sum) => sum,
None => return Err(RawBoundsError::new()),
};
super::const_check::i64(sum)
}
}
#[derive(
Clone, Copy, Debug, Default, Eq, Hash, PartialEq, PartialOrd, Ord,
)]
#[repr(i8)]
#[allow(missing_docs)]
pub enum Sign {
#[default]
Zero = 0,
Positive = 1,
Negative = -1,
}
impl Sign {
#[inline]
pub const fn is_zero(self) -> bool {
matches!(self, Sign::Zero)
}
#[inline]
pub const fn is_positive(self) -> bool {
matches!(self, Sign::Positive)
}
#[inline]
pub const fn is_negative(self) -> bool {
matches!(self, Sign::Negative)
}
#[inline]
pub const fn signum(self) -> i8 {
self.as_i8()
}
#[inline]
pub const fn as_i8(self) -> i8 {
self as i8
}
#[inline]
pub const fn as_i16(self) -> i16 {
self as i16
}
#[inline]
pub const fn as_i32(self) -> i32 {
self as i32
}
#[inline]
pub const fn as_i64(self) -> i64 {
self as i64
}
#[inline]
pub const fn as_i128(self) -> i128 {
self as i128
}
#[inline]
pub fn from_ordinals<T: Ord>(t1: T, t2: T) -> Sign {
use core::cmp::Ordering::*;
match t1.cmp(&t2) {
Less => Sign::Negative,
Equal => Sign::Zero,
Greater => Sign::Positive,
}
}
}
impl core::fmt::Display for Sign {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
if self.is_negative() {
f.write_str("-")
} else {
Ok(())
}
}
}
impl core::ops::Neg for Sign {
type Output = Sign;
#[inline]
fn neg(self) -> Sign {
match self {
Sign::Positive => Sign::Negative,
Sign::Zero => Sign::Zero,
Sign::Negative => Sign::Positive,
}
}
}
impl From<i8> for Sign {
#[inline]
fn from(n: i8) -> Sign {
Sign::from(i64::from(n))
}
}
impl From<i16> for Sign {
#[inline]
fn from(n: i16) -> Sign {
Sign::from(i64::from(n))
}
}
impl From<i32> for Sign {
#[inline]
fn from(n: i32) -> Sign {
Sign::from(i64::from(n))
}
}
impl From<i64> for Sign {
#[inline]
fn from(n: i64) -> Sign {
if n == 0 {
Sign::Zero
} else if n > 0 {
Sign::Positive
} else {
Sign::Negative
}
}
}
impl From<i128> for Sign {
#[inline]
fn from(n: i128) -> Sign {
if n == 0 {
Sign::Zero
} else if n > 0 {
Sign::Positive
} else {
Sign::Negative
}
}
}
impl From<f64> for Sign {
#[inline]
fn from(n: f64) -> Sign {
use core::num::FpCategory::*;
if matches!(n.classify(), Nan | Zero) {
Sign::Zero
} else if n.is_sign_positive() {
Sign::Positive
} else {
Sign::Negative
}
}
}
impl core::ops::Mul<Sign> for Sign {
type Output = Sign;
#[inline]
fn mul(self, rhs: Sign) -> Sign {
match (self, rhs) {
(Sign::Zero, _) | (_, Sign::Zero) => Sign::Zero,
(Sign::Positive, Sign::Positive) => Sign::Positive,
(Sign::Negative, Sign::Negative) => Sign::Positive,
(Sign::Positive, Sign::Negative) => Sign::Negative,
(Sign::Negative, Sign::Positive) => Sign::Negative,
}
}
}
impl core::ops::Mul<i8> for Sign {
type Output = i8;
#[inline]
fn mul(self, n: i8) -> i8 {
self.as_i8() * n
}
}
impl core::ops::Mul<Sign> for i8 {
type Output = i8;
#[inline]
fn mul(self, n: Sign) -> i8 {
self * n.as_i8()
}
}
impl core::ops::Mul<i16> for Sign {
type Output = i16;
#[inline]
fn mul(self, n: i16) -> i16 {
self.as_i16() * n
}
}
impl core::ops::Mul<Sign> for i16 {
type Output = i16;
#[inline]
fn mul(self, n: Sign) -> i16 {
self * n.as_i16()
}
}
impl core::ops::Mul<i32> for Sign {
type Output = i32;
#[inline]
fn mul(self, n: i32) -> i32 {
self.as_i32() * n
}
}
impl core::ops::Mul<Sign> for i32 {
type Output = i32;
#[inline]
fn mul(self, n: Sign) -> i32 {
self * n.as_i32()
}
}
impl core::ops::Mul<i64> for Sign {
type Output = i64;
#[inline]
fn mul(self, n: i64) -> i64 {
self.as_i64() * n
}
}
impl core::ops::Mul<Sign> for i64 {
type Output = i64;
#[inline]
fn mul(self, n: Sign) -> i64 {
self * n.as_i64()
}
}
impl core::ops::Mul<i128> for Sign {
type Output = i128;
#[inline]
fn mul(self, n: i128) -> i128 {
self.as_i128() * n
}
}
impl core::ops::Mul<Sign> for i128 {
type Output = i128;
#[inline]
fn mul(self, n: Sign) -> i128 {
self * n.as_i128()
}
}
const fn next_multiple_of(lhs: i64, rhs: i64) -> i64 {
if rhs == -1 {
return lhs;
}
let r = lhs % rhs;
let m = if (r > 0 && rhs < 0) || (r < 0 && rhs > 0) { r + rhs } else { r };
if m == 0 {
lhs
} else {
lhs + (rhs - m)
}
}