speedate/
lib.rs

1#![doc = include_str ! ("../README.md")]
2extern crate core;
3extern crate strum;
4
5use strum::{Display, EnumMessage};
6
7mod config;
8mod date;
9mod datetime;
10mod duration;
11mod numbers;
12mod time;
13mod util;
14
15pub use config::{
16    DateConfig, DateConfigBuilder, DateTimeConfig, DateTimeConfigBuilder, TimeConfig, TimeConfigBuilder, TimestampUnit,
17};
18pub use date::Date;
19pub use datetime::DateTime;
20pub use duration::Duration;
21pub use time::{MicrosecondsPrecisionOverflowBehavior, Time};
22
23pub use numbers::{float_parse_bytes, float_parse_str, int_parse_bytes, int_parse_str, IntFloat};
24
25// Parsing datetime, date, time & duration values
26
27// get a character from the bytes as as a decimal
28macro_rules! get_digit {
29    ($bytes:ident, $index:expr, $error:ident) => {
30        match $bytes.get($index) {
31            Some(c) if c.is_ascii_digit() => c - b'0',
32            _ => return Err(ParseError::$error),
33        }
34    };
35}
36pub(crate) use get_digit;
37
38// as above without bounds check, requires length to checked first!
39macro_rules! get_digit_unchecked {
40    ($bytes:ident, $index:expr, $error:ident) => {
41        match $bytes.get_unchecked($index) {
42            c if c.is_ascii_digit() => c - b'0',
43            _ => return Err(ParseError::$error),
44        }
45    };
46}
47pub(crate) use get_digit_unchecked;
48
49/// Details about errors when parsing datetime, date, time & duration values
50///
51/// As well as comparing enum values, machine and human readable representations of
52/// errors are provided.
53///
54/// # Examples
55/// (Note: the `strum::EnumMessage` trait must be used to support `.get_documentation()`)
56/// ```
57/// use strum::EnumMessage;
58/// use speedate::{Date, ParseError};
59///
60/// match Date::parse_str("invalid") {
61///      Ok(_) => println!("Parsed successfully"),
62///      Err(error) => {
63///          assert_eq!(error, ParseError::TooShort);
64///          assert_eq!(error.to_string(), "too_short");
65///          assert_eq!(error.get_documentation(), Some("input is too short"));
66///      }
67/// };
68/// ```
69#[derive(Debug, Display, EnumMessage, PartialEq, Eq, Clone)]
70#[strum(serialize_all = "snake_case")]
71pub enum ParseError {
72    /// input is too short
73    TooShort,
74    /// unexpected extra characters at the end of the input
75    ExtraCharacters,
76    /// invalid datetime separator, expected `T`, `t`, `_` or space
77    InvalidCharDateTimeSep,
78    /// invalid date separator, expected `-`
79    InvalidCharDateSep,
80    /// Timestamp is not an exact date
81    DateNotExact,
82    /// invalid character in year
83    InvalidCharYear,
84    /// invalid character in month
85    InvalidCharMonth,
86    /// invalid character in day
87    InvalidCharDay,
88    /// invalid time separator, expected `:`
89    InvalidCharTimeSep,
90    /// invalid character in hour
91    InvalidCharHour,
92    /// invalid character in minute
93    InvalidCharMinute,
94    /// invalid character in second
95    InvalidCharSecond,
96    /// invalid character in second fraction
97    InvalidCharSecondFraction,
98    /// invalid timezone sign
99    InvalidCharTzSign,
100    /// invalid timezone hour
101    InvalidCharTzHour,
102    /// invalid timezone minute
103    InvalidCharTzMinute,
104    /// timezone minute value is outside expected range of 0-59
105    OutOfRangeTzMinute,
106    /// timezone offset must be less than 24 hours
107    OutOfRangeTz,
108    /// timezone is required to adjust to a new timezone
109    TzRequired,
110    /// Error getting system time
111    SystemTimeError,
112    /// month value is outside expected range of 1-12
113    OutOfRangeMonth,
114    /// day value is outside expected range
115    OutOfRangeDay,
116    /// hour value is outside expected range of 0-23
117    OutOfRangeHour,
118    /// minute value is outside expected range of 0-59
119    OutOfRangeMinute,
120    /// second value is outside expected range of 0-59
121    OutOfRangeSecond,
122    /// second fraction value is more than 6 digits long
123    SecondFractionTooLong,
124    /// second fraction digits missing after `.`
125    SecondFractionMissing,
126    /// millisecond fraction value is more than 3 digits long
127    MillisecondFractionTooLong,
128    /// invalid digit in duration
129    DurationInvalidNumber,
130    /// `t` character repeated in duration
131    DurationTRepeated,
132    /// quantity fraction invalid in duration
133    DurationInvalidFraction,
134    /// quantity invalid in time part of duration
135    DurationInvalidTimeUnit,
136    /// quantity invalid in date part of duration
137    DurationInvalidDateUnit,
138    /// "day" identifier in duration not correctly formatted
139    DurationInvalidDays,
140    /// a numeric value in the duration is too large
141    DurationValueTooLarge,
142    /// durations may not exceed 999,999,999 hours
143    DurationHourValueTooLarge,
144    /// durations may not exceed 999,999,999 days
145    DurationDaysTooLarge,
146    /// dates before 0000 are not supported as unix timestamps
147    DateTooSmall,
148    /// dates after 9999 are not supported as unix timestamps
149    DateTooLarge,
150    /// numeric times may not exceed 86,399 seconds
151    TimeTooLarge,
152}
153
154#[derive(Debug, Display, EnumMessage, PartialEq, Eq, Clone)]
155#[strum(serialize_all = "snake_case")]
156pub enum ConfigError {
157    // SecondsPrecisionOverflowBehavior string representation, must be one of "error" or "truncate"
158    UnknownMicrosecondsPrecisionOverflowBehaviorString,
159    // TimestampUnit string representation, must be one of "s", "ms" or "infer"
160    UnknownTimestampUnitString,
161}
162
163/// Used internally to write numbers to a buffer for `Display` of speedate types
164fn display_num_buf(num: usize, start: usize, value: u32, buf: &mut [u8]) {
165    for i in 0..num {
166        if (i + 1) == num {
167            buf[i + start] = b'0' + (value % 10) as u8;
168        } else if num <= 2 {
169            buf[i + start] = b'0' + (value / (10i32.pow((num - 1 - i) as u32)) as u32) as u8;
170        } else {
171            buf[i + start] = b'0' + (value / (10i32.pow((num - 1 - i) as u32)) as u32 % 10) as u8;
172        }
173    }
174}