pub mod consts;
mod line_proto_term;
pub mod read_query;
pub mod write_query;
use std::convert::Infallible;
use std::fmt;
use crate::{Error, WriteQuery};
use consts::{
MILLIS_PER_SECOND, MINUTES_PER_HOUR, NANOS_PER_MICRO, NANOS_PER_MILLI, SECONDS_PER_MINUTE,
};
#[cfg(feature = "derive")]
pub use influxdb_derive::InfluxDbWriteable;
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum Timestamp {
Nanoseconds(u128),
Microseconds(u128),
Milliseconds(u128),
Seconds(u128),
Minutes(u128),
Hours(u128),
}
impl Timestamp {
pub fn nanos(&self) -> u128 {
match self {
Timestamp::Hours(h) => {
h * MINUTES_PER_HOUR * SECONDS_PER_MINUTE * MILLIS_PER_SECOND * NANOS_PER_MILLI
}
Timestamp::Minutes(m) => m * SECONDS_PER_MINUTE * MILLIS_PER_SECOND * NANOS_PER_MILLI,
Timestamp::Seconds(s) => s * MILLIS_PER_SECOND * NANOS_PER_MILLI,
Timestamp::Milliseconds(millis) => millis * NANOS_PER_MILLI,
Timestamp::Microseconds(micros) => micros * NANOS_PER_MICRO,
Timestamp::Nanoseconds(nanos) => *nanos,
}
}
}
impl fmt::Display for Timestamp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use Timestamp::*;
match self {
Nanoseconds(ts) | Microseconds(ts) | Milliseconds(ts) | Seconds(ts) | Minutes(ts)
| Hours(ts) => write!(f, "{ts}"),
}
}
}
#[cfg(feature = "chrono")]
impl TryFrom<Timestamp> for chrono::DateTime<chrono::Utc> {
type Error = <i64 as TryFrom<u128>>::Error;
fn try_from(ts: Timestamp) -> Result<Self, Self::Error> {
use chrono::TimeZone as _;
Ok(chrono::Utc.timestamp_nanos(ts.nanos().try_into()?))
}
}
#[cfg(feature = "chrono")]
impl TryFrom<chrono::DateTime<chrono::Utc>> for Timestamp {
type Error = crate::error::TimeTryFromError<
crate::error::TimestampTooLargeError,
<u128 as TryFrom<i64>>::Error,
>;
fn try_from(dt: chrono::DateTime<chrono::Utc>) -> Result<Self, Self::Error> {
let nanos = dt
.timestamp_nanos_opt()
.ok_or(Self::Error::TimeError(
crate::error::TimestampTooLargeError(()),
))?
.try_into()
.map_err(Self::Error::IntError)?;
Ok(Self::Nanoseconds(nanos))
}
}
#[cfg(feature = "time")]
impl TryFrom<Timestamp> for time::UtcDateTime {
type Error =
crate::error::TimeTryFromError<time::error::ComponentRange, <i128 as TryFrom<u128>>::Error>;
fn try_from(value: Timestamp) -> Result<Self, Self::Error> {
let nanos = value.nanos().try_into().map_err(Self::Error::IntError)?;
time::UtcDateTime::from_unix_timestamp_nanos(nanos).map_err(Self::Error::TimeError)
}
}
#[cfg(feature = "time")]
impl TryFrom<time::UtcDateTime> for Timestamp {
type Error = <u128 as TryFrom<i128>>::Error;
fn try_from(value: time::UtcDateTime) -> Result<Self, Self::Error> {
Ok(Timestamp::Nanoseconds(
value.unix_timestamp_nanos().try_into()?,
))
}
}
pub trait Query {
fn build(&self) -> Result<ValidQuery, Error>;
fn build_with_opts(&self, use_v2: bool) -> Result<ValidQuery, Error>;
fn get_type(&self) -> QueryType;
}
impl<Q: Query> Query for &Q {
fn build(&self) -> Result<ValidQuery, Error> {
Q::build_with_opts(self, false)
}
fn build_with_opts(&self, use_v2: bool) -> Result<ValidQuery, Error> {
Q::build_with_opts(self, use_v2)
}
fn get_type(&self) -> QueryType {
Q::get_type(self)
}
}
impl<Q: Query> Query for Box<Q> {
fn build(&self) -> Result<ValidQuery, Error> {
Q::build(self)
}
fn build_with_opts(&self, use_v2: bool) -> Result<ValidQuery, Error> {
Q::build_with_opts(self, use_v2)
}
fn get_type(&self) -> QueryType {
Q::get_type(self)
}
}
pub trait InfluxDbWriteable {
type Error;
fn try_into_query<I: Into<String>>(self, name: I) -> Result<WriteQuery, Self::Error>;
}
impl InfluxDbWriteable for Timestamp {
type Error = Infallible;
fn try_into_query<I: Into<String>>(self, name: I) -> Result<WriteQuery, Infallible> {
Ok(WriteQuery::new(self, name.into()))
}
}
#[derive(Debug)]
#[doc(hidden)]
pub struct ValidQuery(String);
impl ValidQuery {
pub fn get(self) -> String {
self.0
}
}
impl<T> From<T> for ValidQuery
where
T: Into<String>,
{
fn from(string: T) -> Self {
Self(string.into())
}
}
impl PartialEq<String> for ValidQuery {
fn eq(&self, other: &String) -> bool {
&self.0 == other
}
}
impl PartialEq<&str> for ValidQuery {
fn eq(&self, other: &&str) -> bool {
&self.0 == other
}
}
#[derive(PartialEq, Eq, Debug)]
pub enum QueryType {
ReadQuery,
WriteQuery(String),
}
#[cfg(test)]
mod tests {
#[cfg(feature = "chrono")]
use super::consts::{
MILLIS_PER_SECOND, MINUTES_PER_HOUR, NANOS_PER_MICRO, NANOS_PER_MILLI, SECONDS_PER_MINUTE,
};
use crate::query::{Timestamp, ValidQuery};
#[test]
fn test_equality_str() {
assert_eq!(ValidQuery::from("hello"), "hello");
}
#[test]
fn test_equality_string() {
assert_eq!(
ValidQuery::from(String::from("hello")),
String::from("hello")
);
}
#[test]
fn test_format_for_timestamp_else() {
assert!(format!("{}", Timestamp::Nanoseconds(100)) == "100");
}
#[cfg(feature = "chrono")]
#[test]
fn test_chrono_datetime_from_timestamp_hours() {
use chrono::prelude::*;
let datetime_from_timestamp: DateTime<Utc> = Timestamp::Hours(2).try_into().unwrap();
assert_eq!(
Utc.timestamp_nanos(
(2 * MINUTES_PER_HOUR * SECONDS_PER_MINUTE * MILLIS_PER_SECOND * NANOS_PER_MILLI)
.try_into()
.unwrap()
),
datetime_from_timestamp
)
}
#[cfg(feature = "chrono")]
#[test]
fn test_chrono_datetime_from_timestamp_minutes() {
use chrono::prelude::*;
let datetime_from_timestamp: DateTime<Utc> = Timestamp::Minutes(2).try_into().unwrap();
assert_eq!(
Utc.timestamp_nanos(
(2 * SECONDS_PER_MINUTE * MILLIS_PER_SECOND * NANOS_PER_MILLI)
.try_into()
.unwrap()
),
datetime_from_timestamp
)
}
#[cfg(feature = "chrono")]
#[test]
fn test_chrono_datetime_from_timestamp_seconds() {
use chrono::prelude::*;
let datetime_from_timestamp: DateTime<Utc> = Timestamp::Seconds(2).try_into().unwrap();
assert_eq!(
Utc.timestamp_nanos(
(2 * MILLIS_PER_SECOND * NANOS_PER_MILLI)
.try_into()
.unwrap()
),
datetime_from_timestamp
)
}
#[cfg(feature = "chrono")]
#[test]
fn test_chrono_datetime_from_timestamp_millis() {
use chrono::prelude::*;
let datetime_from_timestamp: DateTime<Utc> = Timestamp::Milliseconds(2).try_into().unwrap();
assert_eq!(
Utc.timestamp_nanos((2 * NANOS_PER_MILLI).try_into().unwrap()),
datetime_from_timestamp
)
}
#[cfg(feature = "chrono")]
#[test]
fn test_chrono_datetime_from_timestamp_nanos() {
use chrono::prelude::*;
let datetime_from_timestamp: DateTime<Utc> = Timestamp::Nanoseconds(1).try_into().unwrap();
assert_eq!(Utc.timestamp_nanos(1), datetime_from_timestamp)
}
#[cfg(feature = "chrono")]
#[test]
fn test_chrono_datetime_from_timestamp_micros() {
use chrono::prelude::*;
let datetime_from_timestamp: DateTime<Utc> = Timestamp::Microseconds(2).try_into().unwrap();
assert_eq!(
Utc.timestamp_nanos((2 * NANOS_PER_MICRO).try_into().unwrap()),
datetime_from_timestamp
)
}
#[cfg(feature = "chrono")]
#[test]
fn test_timestamp_from_chrono_date() {
use chrono::prelude::*;
let timestamp_from_datetime: Timestamp = Utc
.with_ymd_and_hms(1970, 1, 1, 0, 0, 1)
.single()
.unwrap()
.try_into()
.unwrap();
assert_eq!(
Timestamp::Nanoseconds(MILLIS_PER_SECOND * NANOS_PER_MILLI),
timestamp_from_datetime
)
}
}