Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#7] Add feature flag for heavy dependencies #51

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@ edition = "2021"
version = "0.9.4"
license = "MIT OR Apache-2.0"

[features]
parser = ["dep:regex", "dep:lazy_static"]
formatter = ["dep:chrono", "dep:serde"]

[dependencies]
chrono = "0.4.31"
chrono = { version = "0.4.31", optional = true }
derive_more = "0.99.17"
lazy_static = "1.4.0"
regex = "1.10.2"
serde = { version = "1.0.193", features = ["derive"], default-features = false }
lazy_static = { version = "1.4.0", optional = true }
regex = { version = "1.10.2", optional = true }
serde = { version = "1.0.193", features = ["derive"], default-features = false, optional=true }
thiserror = "1.0.50"

[dev-dependencies]
Expand Down
83 changes: 46 additions & 37 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,31 +53,32 @@ use std::ops::Mul;
use std::ops::MulAssign;
use std::ops::Sub;
use std::ops::SubAssign;
#[cfg(feature = "parser")]
use std::str::FromStr;
use std::time::SystemTime;

use chrono::format::DelayedFormat;
use chrono::format::StrftimeItems;
use chrono::DateTime;
use chrono::NaiveDateTime;
use derive_more::Deref;
use derive_more::From;
use derive_more::Into;
use derive_more::Neg;
use derive_more::Sum;
#[cfg(feature = "parser")]
use lazy_static::lazy_static;
#[cfg(feature = "parser")]
use regex::Regex;
#[cfg(feature = "formatter")]
use serde::de::Visitor;
#[cfg(feature = "formatter")]
use serde::Deserialize;
#[cfg(feature = "formatter")]
use serde::Serialize;
use thiserror::Error;

/// A point in time.
///
/// Low overhead time representation. Internally represented as milliseconds.
#[derive(
Eq, PartialEq, Hash, Ord, PartialOrd, Copy, Clone, Default, Serialize, Deref, From, Into,
)]
#[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Copy, Clone, Default, Deref, From, Into)]
#[cfg_attr(feature = "formatter", derive(Serialize))]
pub struct Time(i64);

impl Time {
Expand All @@ -104,6 +105,7 @@ impl Time {
Time::millis(hours * Self::HOUR.0)
}

#[cfg(feature = "formatter")]
/// Returns an RFC 3339 and ISO 8601 date and time string such as
/// 1996-12-19T16:39:57+00:00.
///
Expand All @@ -119,6 +121,7 @@ impl Time {
self.format("%Y-%m-%dT%H:%M:%S+00:00").to_string()
}

#[cfg(feature = "formatter")]
/// The function format string is forwarded to
/// [`chrono::NaiveDateTime::format()`]
///
Expand All @@ -130,7 +133,10 @@ impl Time {
/// use tinytime::Time;
/// assert_eq!("∞", Time::MAX.format("whatever").to_string());
/// ```
pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> {
pub fn format<'a>(
&self,
fmt: &'a str,
) -> chrono::format::DelayedFormat<chrono::format::StrftimeItems<'a>> {
let secs = self.0 / 1000;
let nanos = (self.0 % 1000) * 1_000_000;
// casting to u32 is safe here because it is guaranteed that the value is in
Expand All @@ -142,13 +148,18 @@ impl Time {
nanos.unsigned_abs()
} as u32;

let t = NaiveDateTime::from_timestamp_opt(secs, nanos);
let t = chrono::NaiveDateTime::from_timestamp_opt(secs, nanos);
match t {
None => DelayedFormat::new(None, None, StrftimeItems::new("∞")),
None => chrono::format::DelayedFormat::new(
None,
None,
chrono::format::StrftimeItems::new("∞"),
),
Some(v) => v.format(fmt),
}
}

#[cfg(feature = "formatter")]
/// Parses an RFC 3339 date and time string into a [Time] instance.
///
/// The parsing is forwarded to [`chrono::DateTime::parse_from_rfc3339()`].
Expand All @@ -164,7 +175,7 @@ impl Time {
/// );
/// ```
pub fn parse_from_rfc3339(s: &str) -> Result<Time, chrono::ParseError> {
DateTime::parse_from_rfc3339(s)
chrono::DateTime::parse_from_rfc3339(s)
.map(|chrono_datetime| Time::millis(chrono_datetime.timestamp_millis()))
}

Expand Down Expand Up @@ -257,6 +268,7 @@ impl Time {
}
}

#[cfg(feature = "formatter")]
impl Display for Time {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let rfc3339_string = self.to_rfc3339();
Expand All @@ -275,6 +287,7 @@ impl TryFrom<Duration> for Time {
}
}

#[cfg(feature = "formatter")]
/// Allows deserializing from RFC 3339 strings and unsigned integers.
impl<'de> Deserialize<'de> for Time {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
Expand All @@ -285,8 +298,9 @@ impl<'de> Deserialize<'de> for Time {
}
}

#[cfg(feature = "formatter")]
struct TimeVisitor;

#[cfg(feature = "formatter")]
impl<'de> Visitor<'de> for TimeVisitor {
type Value = Time;

Expand Down Expand Up @@ -382,7 +396,8 @@ pub enum TimeWindowError {
/// Debug-asserts ensure that start <= end.
/// If compiled in release mode, the invariant of start <= end is maintained, by
/// correcting invalid use of the API (and setting end to start).
#[derive(Clone, Debug, Eq, PartialEq, Default, Copy, Serialize, Deserialize, From, Into, Hash)]
#[derive(Clone, Debug, Eq, PartialEq, Default, Copy, From, Into, Hash)]
#[cfg_attr(feature = "formatter", derive(Serialize, Deserialize))]
pub struct TimeWindow {
start: Time,
end: Time,
Expand Down Expand Up @@ -828,22 +843,9 @@ impl TimeWindow {
/// Duration can be negative. Internally duration is represented as
/// milliseconds.
#[derive(
Eq,
PartialEq,
Ord,
PartialOrd,
Copy,
Clone,
Debug,
Default,
Hash,
Serialize,
Deref,
From,
Into,
Sum,
Neg,
Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Debug, Default, Hash, Deref, From, Into, Sum, Neg,
)]
#[cfg_attr(feature = "formatter", derive(Serialize))]
pub struct Duration(i64);

impl Duration {
Expand Down Expand Up @@ -925,6 +927,7 @@ impl Duration {
}
}

#[cfg(feature = "parser")]
/// Allows deserializing from strings, unsigned integers, and signed integers.
impl<'de> Deserialize<'de> for Duration {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
Expand All @@ -947,8 +950,9 @@ impl PartialOrd<std::time::Duration> for Duration {
}
}

#[cfg(feature = "parser")]
struct DurationVisitor;

#[cfg(feature = "parser")]
impl<'de> Visitor<'de> for DurationVisitor {
type Value = Duration;

Expand Down Expand Up @@ -1289,6 +1293,7 @@ impl From<std::time::Duration> for Duration {
}
}

#[cfg(feature = "parser")]
/// Parses Duration from str
///
/// # Example
Expand Down Expand Up @@ -1318,7 +1323,7 @@ impl FromStr for Duration {

fn from_str(seconds: &str) -> Result<Self, Self::Err> {
lazy_static! {
static ref RE: Regex = Regex::new(REGEX).unwrap();
static ref RE: Regex = Regex::new(DURATION_REGEX).unwrap();
}
let captures = RE
.captures(seconds)
Expand All @@ -1343,6 +1348,10 @@ impl FromStr for Duration {
}
}

#[cfg(feature = "parser")]
const DURATION_REGEX: &str =
r"^(?P<sign>-)?((?P<h>\d+)h)?((?P<m>\d+)m)?((?P<s>\d+)s)?((?P<ms>\d+)ms)?$";

#[derive(Debug)]
pub enum DurationParseError {
UnrecognizedFormat,
Expand All @@ -1359,16 +1368,12 @@ impl Display for DurationParseError {
}
}

const REGEX: &str = r"^(?P<sign>-)?((?P<h>\d+)h)?((?P<m>\d+)m)?((?P<s>\d+)s)?((?P<ms>\d+)ms)?$";

#[cfg(test)]
mod time_test {
use serde_test::assert_de_tokens;
use serde_test::Token;

use crate::Duration;
use crate::Time;

#[cfg(feature = "formatter")]
#[test]
fn test_display() {
struct TestCase {
Expand Down Expand Up @@ -1498,8 +1503,11 @@ mod time_test {
}
}

#[cfg(feature = "parser")]
#[test]
fn deserialize_time() {
use serde_test::assert_de_tokens;
use serde_test::Token;
// strings
assert_de_tokens(&Time::seconds(7), &[Token::Str("1970-01-01T00:00:07Z")]);
assert_de_tokens(&Time::seconds(7), &[Token::String("1970-01-01T00:00:07Z")]);
Expand Down Expand Up @@ -1559,8 +1567,6 @@ mod time_test {

#[cfg(test)]
mod duration_test {
use serde_test::assert_de_tokens;
use serde_test::Token;

use super::*;

Expand Down Expand Up @@ -1714,8 +1720,11 @@ mod duration_test {
assert_eq!(Duration::minutes(-4), time2 - time);
}

#[cfg(feature = "parser")]
#[test]
fn deserialize_duration() {
use serde_test::assert_de_tokens;
use serde_test::Token;
// strings
assert_de_tokens(&Duration::minutes(7), &[Token::Str("7m")]);
assert_de_tokens(
Expand Down
Loading