use std::error::Error;
use chrono::DateTime;
#[derive(Copy, Clone, Debug, PartialEq, Eq, clap::ValueEnum)]
pub enum RoundMode {
Round,
Floor,
Ceil,
Trunc,
}
#[derive(Clone, Debug)]
pub struct KeyOpts {
pub time_format: String,
pub decimals: u32,
pub round_mode: RoundMode,
}
impl Default for KeyOpts {
fn default() -> Self {
Self { time_format: "%Y-%m-%d".to_string(), decimals: 3, round_mode: RoundMode::Round }
}
}
pub type DupKey = (String, i64, i64);
impl KeyOpts {
pub fn validate(&self) -> Result<(), Box<dyn Error>> {
use chrono::format::{Item, StrftimeItems};
if StrftimeItems::new(&self.time_format).any(|i| matches!(i, Item::Error)) {
return Err(format!("invalid --time-format '{}'", self.time_format).into());
}
Ok(())
}
pub fn key(&self, ts_ms: Option<i64>, lon: f64, lat: f64) -> Option<DupKey> {
let ts = ts_ms?;
if lon.is_nan() || lat.is_nan() {
return None;
}
let dt = DateTime::from_timestamp_millis(ts)?;
let time = dt.naive_utc().format(&self.time_format).to_string();
Some((time, self.round(lon), self.round(lat)))
}
fn round(&self, v: f64) -> i64 {
let x = v * 10f64.powi(self.decimals as i32);
let r = match self.round_mode {
RoundMode::Round => x.round(),
RoundMode::Floor => x.floor(),
RoundMode::Ceil => x.ceil(),
RoundMode::Trunc => x.trunc(),
};
r as i64
}
}