use std::{collections::HashMap, sync::Arc};
use chrono::{DateTime, TimeZone, Utc};
use hamelin_lib::tree::ast::query::Query;
use crate::incremental::TimeRange;
use hamelin_lib::tree::builder::query as build_query;
use hamelin_lib::type_check_with_provider;
use hamelin_lib::{
provider::EnvironmentProvider,
tree::{
ast::identifier::{Identifier, SimpleIdentifier},
builder::pipeline::PipelineBuilder,
typed_ast::{pipeline::TypedPipeline, query::TypedStatement},
},
types::{array::Array, struct_type::Struct, INT, STRING, TIMESTAMP},
};
pub const TIMESTAMP_FIELD: &str = "timestamp";
pub fn ts(s: &str) -> DateTime<Utc> {
let parts: Vec<&str> = s.split(' ').collect();
let date_parts: Vec<&str> = parts[0].split('-').collect();
let time_parts: Vec<&str> = parts[1].split(':').collect();
let year = date_parts[0].parse::<i32>().unwrap();
let month = date_parts[1].parse::<u32>().unwrap();
let day = date_parts[2].parse::<u32>().unwrap();
let hour = time_parts[0].parse::<u32>().unwrap();
let minute = time_parts[1].parse::<u32>().unwrap();
let second = time_parts[2].parse::<u32>().unwrap();
Utc.with_ymd_and_hms(year, month, day, hour, minute, second)
.unwrap()
}
pub fn time_range(start: &str, end: &str) -> TimeRange {
(ts(start)..=ts(end)).into()
}
pub fn time_range_exclusive(start: &str, end: &str) -> TimeRange {
(ts(start)..ts(end)).into()
}
pub fn stale_ranges(
table_name: &str,
start: &str,
end: &str,
) -> HashMap<Identifier, Option<TimeRange>> {
let mut map = HashMap::new();
let simple_id: SimpleIdentifier = table_name.parse().unwrap();
map.insert(simple_id.into(), Some(time_range(start, end)));
map
}
pub fn multi_stale_ranges(ranges: &[(&str, &str, &str)]) -> HashMap<Identifier, Option<TimeRange>> {
let mut map = HashMap::new();
for (table_name, start, end) in ranges {
let simple_id: SimpleIdentifier = table_name.parse().unwrap();
map.insert(simple_id.into(), Some(time_range(start, end)));
}
map
}
pub fn timestamp_field() -> SimpleIdentifier {
TIMESTAMP_FIELD.parse().unwrap()
}
#[derive(Debug)]
pub struct MockIncrementalProvider;
impl EnvironmentProvider for MockIncrementalProvider {
fn reflect_columns(&self, name: &Identifier) -> anyhow::Result<Struct> {
let mut env = Struct::default();
let events: Identifier = SimpleIdentifier::new("events").into();
let users: Identifier = SimpleIdentifier::new("users").into();
let other_events: Identifier = SimpleIdentifier::new("other_events").into();
if name == &events {
env = env
.with_str("timestamp", TIMESTAMP)
.with_str("user", STRING)
.with_str("severity", STRING)
.with_str("amount", INT)
.with_str("user_id", STRING)
.with_str("tags", Array::new(STRING).into());
} else if name == &users {
env = env.with_str("id", STRING).with_str("name", STRING);
} else if name == &other_events {
env = env
.with_str("timestamp", TIMESTAMP)
.with_str("data", STRING);
} else {
anyhow::bail!("Unknown table: {:?}", name);
}
Ok(env)
}
fn reflect_datasets(&self) -> anyhow::Result<Vec<Identifier>> {
Ok(vec![
SimpleIdentifier::new("events").into(),
SimpleIdentifier::new("users").into(),
SimpleIdentifier::new("other_events").into(),
])
}
}
pub fn build_pipeline(builder: PipelineBuilder) -> Arc<TypedPipeline> {
build_typed_query(build_query().main(builder.build()).build()).pipeline
}
pub fn build_typed_query(query: Query) -> TypedStatement {
type_check_with_provider(query, Arc::new(MockIncrementalProvider)).output
}