use key_paths_core::KeyPaths;
use std::marker::PhantomData;
use std::time::SystemTime;
#[cfg(feature = "datetime")]
use chrono::{DateTime, TimeZone};
pub struct LazyQuery<'a, T: 'static, I>
where
I: Iterator<Item = &'a T>,
{
iter: I,
_phantom: PhantomData<&'a T>,
}
impl<'a, T: 'static> LazyQuery<'a, T, std::slice::Iter<'a, T>> {
pub fn new(data: &'a [T]) -> Self {
Self {
iter: data.iter(),
_phantom: PhantomData,
}
}
}
impl<'a, T: 'static, I> LazyQuery<'a, T, I>
where
I: Iterator<Item = &'a T>,
{
pub fn from_iter(iter: I) -> Self {
Self {
iter,
_phantom: PhantomData,
}
}
}
impl<'a, T: 'static, I> LazyQuery<'a, T, I>
where
I: Iterator<Item = &'a T> + 'a,
{
pub fn where_<F, P>(self, path: KeyPaths<T, F>, predicate: P) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a>
where
F: 'static,
P: Fn(&F) -> bool + 'a,
{
LazyQuery {
iter: self.iter.filter(move |item| {
path.get(item).map_or(false, |val| predicate(val))
}),
_phantom: PhantomData,
}
}
pub fn map_items<F, O>(self, f: F) -> impl Iterator<Item = O> + 'a
where
F: Fn(&'a T) -> O + 'a,
I: 'a,
{
self.iter.map(f)
}
pub fn select_lazy<F>(self, path: KeyPaths<T, F>) -> impl Iterator<Item = F> + 'a
where
F: Clone + 'static,
I: 'a,
{
self.iter.filter_map(move |item| path.get(item).cloned())
}
pub fn take_lazy(self, n: usize) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a>
where
I: 'a,
{
LazyQuery {
iter: self.iter.take(n),
_phantom: PhantomData,
}
}
pub fn skip_lazy(self, n: usize) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a>
where
I: 'a,
{
LazyQuery {
iter: self.iter.skip(n),
_phantom: PhantomData,
}
}
pub fn collect(self) -> Vec<&'a T> {
self.iter.collect()
}
pub fn first(mut self) -> Option<&'a T> {
self.iter.next()
}
pub fn count(self) -> usize {
self.iter.count()
}
pub fn any(mut self) -> bool {
self.iter.next().is_some()
}
pub fn for_each<F>(self, f: F)
where
F: FnMut(&'a T),
{
self.iter.for_each(f)
}
pub fn fold<B, F>(self, init: B, f: F) -> B
where
F: FnMut(B, &'a T) -> B,
{
self.iter.fold(init, f)
}
pub fn find<P>(mut self, predicate: P) -> Option<&'a T>
where
P: FnMut(&&'a T) -> bool,
{
self.iter.find(predicate)
}
pub fn all_match<P>(mut self, mut predicate: P) -> bool
where
P: FnMut(&'a T) -> bool,
{
self.iter.all(move |item| predicate(item))
}
pub fn all(self) -> Vec<&'a T> {
self.iter.collect()
}
pub fn into_iter(self) -> I {
self.iter
}
}
impl<'a, T: 'static, I> LazyQuery<'a, T, I>
where
I: Iterator<Item = &'a T> + 'a,
{
pub fn sum_by<F>(self, path: KeyPaths<T, F>) -> F
where
F: Clone + std::ops::Add<Output = F> + Default + 'static,
I: 'a,
{
self.iter
.filter_map(move |item| path.get(item).cloned())
.fold(F::default(), |acc, val| acc + val)
}
pub fn avg_by(self, path: KeyPaths<T, f64>) -> Option<f64>
where
I: 'a,
{
let items: Vec<f64> = self
.iter
.filter_map(move |item| path.get(item).cloned())
.collect();
if items.is_empty() {
None
} else {
Some(items.iter().sum::<f64>() / items.len() as f64)
}
}
pub fn min_by<F>(self, path: KeyPaths<T, F>) -> Option<F>
where
F: Ord + Clone + 'static,
I: 'a,
{
self.iter.filter_map(move |item| path.get(item).cloned()).min()
}
pub fn max_by<F>(self, path: KeyPaths<T, F>) -> Option<F>
where
F: Ord + Clone + 'static,
I: 'a,
{
self.iter.filter_map(move |item| path.get(item).cloned()).max()
}
pub fn min_by_float(self, path: KeyPaths<T, f64>) -> Option<f64>
where
I: 'a,
{
self.iter
.filter_map(move |item| path.get(item).cloned())
.min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
}
pub fn max_by_float(self, path: KeyPaths<T, f64>) -> Option<f64>
where
I: 'a,
{
self.iter
.filter_map(move |item| path.get(item).cloned())
.max_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
}
pub fn where_after_systemtime(self, path: KeyPaths<T, SystemTime>, reference: SystemTime) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a> {
self.where_(path, move |time| time > &reference)
}
pub fn where_before_systemtime(self, path: KeyPaths<T, SystemTime>, reference: SystemTime) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a> {
self.where_(path, move |time| time < &reference)
}
pub fn where_between_systemtime(
self,
path: KeyPaths<T, SystemTime>,
start: SystemTime,
end: SystemTime,
) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a> {
self.where_(path, move |time| time >= &start && time <= &end)
}
}
#[cfg(feature = "datetime")]
impl<'a, T: 'static, I> LazyQuery<'a, T, I>
where
I: Iterator<Item = &'a T> + 'a,
{
pub fn where_after<Tz>(self, path: KeyPaths<T, DateTime<Tz>>, reference: DateTime<Tz>) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a>
where
Tz: TimeZone + 'static,
Tz::Offset: std::fmt::Display,
{
self.where_(path, move |time| time > &reference)
}
pub fn where_before<Tz>(self, path: KeyPaths<T, DateTime<Tz>>, reference: DateTime<Tz>) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a>
where
Tz: TimeZone + 'static,
Tz::Offset: std::fmt::Display,
{
self.where_(path, move |time| time < &reference)
}
pub fn where_between<Tz>(
self,
path: KeyPaths<T, DateTime<Tz>>,
start: DateTime<Tz>,
end: DateTime<Tz>,
) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a>
where
Tz: TimeZone + 'static,
Tz::Offset: std::fmt::Display,
{
self.where_(path, move |time| time >= &start && time <= &end)
}
pub fn where_today<Tz>(self, path: KeyPaths<T, DateTime<Tz>>, now: DateTime<Tz>) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a>
where
Tz: TimeZone + 'static,
Tz::Offset: std::fmt::Display,
{
self.where_(path, move |time| {
time.date_naive() == now.date_naive()
})
}
pub fn where_year<Tz>(self, path: KeyPaths<T, DateTime<Tz>>, year: i32) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a>
where
Tz: TimeZone + 'static,
Tz::Offset: std::fmt::Display,
{
use chrono::Datelike;
self.where_(path, move |time| time.year() == year)
}
pub fn where_month<Tz>(self, path: KeyPaths<T, DateTime<Tz>>, month: u32) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a>
where
Tz: TimeZone + 'static,
Tz::Offset: std::fmt::Display,
{
use chrono::Datelike;
self.where_(path, move |time| time.month() == month)
}
pub fn where_day<Tz>(self, path: KeyPaths<T, DateTime<Tz>>, day: u32) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a>
where
Tz: TimeZone + 'static,
Tz::Offset: std::fmt::Display,
{
use chrono::Datelike;
self.where_(path, move |time| time.day() == day)
}
pub fn where_weekend<Tz>(self, path: KeyPaths<T, DateTime<Tz>>) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a>
where
Tz: TimeZone + 'static,
Tz::Offset: std::fmt::Display,
{
use chrono::Datelike;
self.where_(path, |time| {
let weekday = time.weekday().num_days_from_monday();
weekday >= 5
})
}
pub fn where_weekday<Tz>(self, path: KeyPaths<T, DateTime<Tz>>) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a>
where
Tz: TimeZone + 'static,
Tz::Offset: std::fmt::Display,
{
use chrono::Datelike;
self.where_(path, |time| {
let weekday = time.weekday().num_days_from_monday();
weekday < 5
})
}
pub fn where_business_hours<Tz>(self, path: KeyPaths<T, DateTime<Tz>>) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a>
where
Tz: TimeZone + 'static,
Tz::Offset: std::fmt::Display,
{
use chrono::Timelike;
self.where_(path, |time| {
let hour = time.hour();
hour >= 9 && hour < 17
})
}
}
impl<'a, T: 'static, I> LazyQuery<'a, T, I>
where
I: Iterator<Item = &'a T> + 'a,
{
pub fn min_timestamp(self, path: KeyPaths<T, i64>) -> Option<i64>
where
I: 'a,
{
self.iter
.filter_map(move |item| path.get(item).cloned())
.min()
}
pub fn max_timestamp(self, path: KeyPaths<T, i64>) -> Option<i64>
where
I: 'a,
{
self.iter
.filter_map(move |item| path.get(item).cloned())
.max()
}
pub fn avg_timestamp(self, path: KeyPaths<T, i64>) -> Option<i64>
where
I: 'a,
{
let items: Vec<i64> = self
.iter
.filter_map(move |item| path.get(item).cloned())
.collect();
if items.is_empty() {
None
} else {
Some(items.iter().sum::<i64>() / items.len() as i64)
}
}
pub fn sum_timestamp(self, path: KeyPaths<T, i64>) -> i64
where
I: 'a,
{
self.iter
.filter_map(move |item| path.get(item).cloned())
.sum()
}
pub fn count_timestamp(self, path: KeyPaths<T, i64>) -> usize
where
I: 'a,
{
self.iter
.filter(move |item| path.get(item).is_some())
.count()
}
pub fn where_after_timestamp(self, path: KeyPaths<T, i64>, reference: i64) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a> {
self.where_(path, move |timestamp| timestamp > &reference)
}
pub fn where_before_timestamp(self, path: KeyPaths<T, i64>, reference: i64) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a> {
self.where_(path, move |timestamp| timestamp < &reference)
}
pub fn where_between_timestamp(
self,
path: KeyPaths<T, i64>,
start: i64,
end: i64,
) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a> {
self.where_(path, move |timestamp| timestamp >= &start && timestamp <= &end)
}
pub fn where_last_days_timestamp(self, path: KeyPaths<T, i64>, days: i64) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a> {
let now = chrono::Utc::now().timestamp_millis();
let cutoff = now - (days * 24 * 60 * 60 * 1000); self.where_after_timestamp(path, cutoff)
}
pub fn where_next_days_timestamp(self, path: KeyPaths<T, i64>, days: i64) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a> {
let now = chrono::Utc::now().timestamp_millis();
let cutoff = now + (days * 24 * 60 * 60 * 1000); self.where_before_timestamp(path, cutoff)
}
pub fn where_last_hours_timestamp(self, path: KeyPaths<T, i64>, hours: i64) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a> {
let now = chrono::Utc::now().timestamp_millis();
let cutoff = now - (hours * 60 * 60 * 1000); self.where_after_timestamp(path, cutoff)
}
pub fn where_next_hours_timestamp(self, path: KeyPaths<T, i64>, hours: i64) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a> {
let now = chrono::Utc::now().timestamp_millis();
let cutoff = now + (hours * 60 * 60 * 1000); self.where_before_timestamp(path, cutoff)
}
pub fn where_last_minutes_timestamp(self, path: KeyPaths<T, i64>, minutes: i64) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a> {
let now = chrono::Utc::now().timestamp_millis();
let cutoff = now - (minutes * 60 * 1000); self.where_after_timestamp(path, cutoff)
}
pub fn where_next_minutes_timestamp(self, path: KeyPaths<T, i64>, minutes: i64) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a> {
let now = chrono::Utc::now().timestamp_millis();
let cutoff = now + (minutes * 60 * 1000); self.where_before_timestamp(path, cutoff)
}
}
impl<'a, T: 'static, I> IntoIterator for LazyQuery<'a, T, I>
where
I: Iterator<Item = &'a T>,
{
type Item = &'a T;
type IntoIter = I;
fn into_iter(self) -> Self::IntoIter {
self.iter
}
}