use chrono::{DateTime, Utc};
use nominal_api::objects::api::TimeUnit as ApiTimeUnit;
use nominal_api::objects::ingest::api::{
AbsoluteTimestamp, CustomTimestamp, EpochTimestamp, Iso8601Timestamp, RelativeTimestamp,
TimestampMetadata, TimestampType,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum TimeUnit {
Nanoseconds,
Microseconds,
Milliseconds,
Seconds,
Minutes,
Hours,
Days,
}
impl TimeUnit {
pub(crate) fn into_conjure(self) -> ApiTimeUnit {
match self {
TimeUnit::Nanoseconds => ApiTimeUnit::Nanoseconds,
TimeUnit::Microseconds => ApiTimeUnit::Microseconds,
TimeUnit::Milliseconds => ApiTimeUnit::Milliseconds,
TimeUnit::Seconds => ApiTimeUnit::Seconds,
TimeUnit::Minutes => ApiTimeUnit::Minutes,
TimeUnit::Hours => ApiTimeUnit::Hours,
TimeUnit::Days => ApiTimeUnit::Days,
}
}
}
#[derive(Debug, Clone)]
pub struct Timestamp {
series_name: String,
kind: TimestampKind,
}
#[derive(Debug, Clone)]
enum TimestampKind {
Iso8601,
Epoch(TimeUnit),
Custom {
format: String,
default_year: Option<i32>,
default_day_of_year: Option<i32>,
},
Relative {
unit: TimeUnit,
offset: Option<DateTime<Utc>>,
},
}
impl Timestamp {
pub fn iso8601(series_name: impl Into<String>) -> Self {
Self {
series_name: series_name.into(),
kind: TimestampKind::Iso8601,
}
}
pub fn epoch(series_name: impl Into<String>, unit: TimeUnit) -> Self {
Self {
series_name: series_name.into(),
kind: TimestampKind::Epoch(unit),
}
}
pub fn custom(series_name: impl Into<String>, format: impl Into<String>) -> Self {
Self {
series_name: series_name.into(),
kind: TimestampKind::Custom {
format: format.into(),
default_year: None,
default_day_of_year: None,
},
}
}
pub fn relative(series_name: impl Into<String>, unit: TimeUnit) -> Self {
Self {
series_name: series_name.into(),
kind: TimestampKind::Relative { unit, offset: None },
}
}
#[must_use]
pub fn with_offset(mut self, offset: DateTime<Utc>) -> Self {
if let TimestampKind::Relative { offset: slot, .. } = &mut self.kind {
*slot = Some(offset);
}
self
}
#[must_use]
pub fn with_default_year(mut self, year: i32) -> Self {
if let TimestampKind::Custom { default_year, .. } = &mut self.kind {
*default_year = Some(year);
}
self
}
#[must_use]
pub fn with_default_day_of_year(mut self, day: i32) -> Self {
if let TimestampKind::Custom {
default_day_of_year,
..
} = &mut self.kind
{
*default_day_of_year = Some(day);
}
self
}
pub(crate) fn into_conjure(self) -> TimestampMetadata {
let ts_type = match self.kind {
TimestampKind::Iso8601 => TimestampType::Absolute(Box::new(
AbsoluteTimestamp::Iso8601(Iso8601Timestamp::new()),
)),
TimestampKind::Epoch(unit) => TimestampType::Absolute(Box::new(
AbsoluteTimestamp::EpochOfTimeUnit(EpochTimestamp::new(unit.into_conjure())),
)),
TimestampKind::Custom {
format,
default_year,
default_day_of_year,
} => {
let mut b = CustomTimestamp::builder().format(format);
if let Some(y) = default_year {
b = b.default_year(y);
}
if let Some(d) = default_day_of_year {
b = b.default_day_of_year(d);
}
TimestampType::Absolute(Box::new(AbsoluteTimestamp::CustomFormat(b.build())))
}
TimestampKind::Relative { unit, offset } => {
let mut b = RelativeTimestamp::builder().time_unit(unit.into_conjure());
if let Some(o) = offset {
b = b.offset(o);
}
TimestampType::Relative(b.build())
}
};
TimestampMetadata::new(self.series_name, ts_type)
}
}