use crate::js::{self, Value};
use crate::obj;
pub const EVENT_DIMENSIONS: [(&str, &str); 3] = [("page", "path"), ("hostname", "hostname"), ("event", "name")];
pub const SESSION_DIMENSIONS: [(&str, &str); 20] = [
("entry", "entry_path"),
("exit", "exit_path"),
("referrer", "referrer_host"),
("source", "source"),
("channel", "channel"),
("utm_source", "utm_source"),
("utm_medium", "utm_medium"),
("utm_campaign", "utm_campaign"),
("utm_term", "utm_term"),
("utm_content", "utm_content"),
("country", "country"),
("region", "region"),
("city", "city"),
("browser", "browser"),
("browser_version", "browser_version"),
("os", "os"),
("os_version", "os_version"),
("device", "device"),
("screen", "screen"),
("language", "language"),
];
pub fn dimensions() -> Vec<&'static str> {
let mut out: Vec<&str> = EVENT_DIMENSIONS.iter().map(|d| d.0).collect();
out.extend(SESSION_DIMENSIONS.iter().map(|d| d.0));
out.push("ai_agent");
out.push("ai_page");
out
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Filter {
pub dimension: String,
pub op: String,
pub value: String,
}
impl Filter {
pub fn to_value(&self) -> Value {
obj! { "dimension" => self.dimension.clone(), "op" => self.op.clone(), "value" => self.value.clone() }
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Query {
pub site: String,
pub from: i64,
pub to: i64,
pub filters: Vec<Filter>,
}
pub fn is_dimension(value: &str) -> bool {
dimensions().contains(&value)
}
pub fn is_session_dimension(value: &str) -> bool {
SESSION_DIMENSIONS.iter().any(|d| d.0 == value)
}
pub fn is_event_dimension(value: &str) -> bool {
EVENT_DIMENSIONS.iter().any(|d| d.0 == value)
}
pub fn column_of(dimension: &str) -> Option<&'static str> {
SESSION_DIMENSIONS.iter().chain(EVENT_DIMENSIONS.iter()).find(|d| d.0 == dimension).map(|d| d.1)
}
pub fn parse_filter(text: &str) -> Option<Filter> {
let first = text.find(':')?;
let second = first + 1 + text[first + 1..].find(':')?;
let dimension = &text[..first];
let op = &text[first + 1..second];
let value = &text[second + 1..];
if !is_session_dimension(dimension) && !is_event_dimension(dimension) {
return None;
}
if op != "is" && op != "not" && op != "contains" {
return None;
}
Some(Filter { dimension: dimension.into(), op: op.into(), value: js::head16(value, 500) })
}
pub const MAX_FILTERS: usize = 6;