use indexmap::IndexMap;
use polars::prelude::*;
use serde::Serialize;
use serde_json::Value as Json;
#[derive(Debug, Serialize)]
pub struct TreeNode {
pub name: String,
pub measures: IndexMap<String, Json>,
pub children: Vec<TreeNode>,
#[serde(skip_serializing_if = "std::ops::Not::not")]
pub is_other: bool,
#[serde(skip)]
pub n_folded: i64,
#[serde(skip)]
pub has_more: bool,
}
pub fn days_to_iso(days: i32) -> String {
let z = days as i64 + 719_468;
let era = if z >= 0 { z } else { z - 146_096 } / 146_097;
let doe = z - era * 146_097;
let yoe = (doe - doe / 1460 + doe / 36_524 - doe / 146_096) / 365;
let y = yoe + era * 400;
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
let mp = (5 * doy + 2) / 153;
let d = doy - (153 * mp + 2) / 5 + 1;
let m = if mp < 10 { mp + 3 } else { mp - 9 };
let y = if m <= 2 { y + 1 } else { y };
format!("{y:04}-{m:02}-{d:02}")
}
pub fn av_to_json(av: &AnyValue) -> Json {
match av {
AnyValue::Null => Json::Null,
AnyValue::Boolean(b) => Json::Bool(*b),
AnyValue::Int8(v) => Json::from(*v as i64),
AnyValue::Int16(v) => Json::from(*v as i64),
AnyValue::Int32(v) => Json::from(*v as i64),
AnyValue::Int64(v) => Json::from(*v),
AnyValue::UInt8(v) => Json::from(*v as u64),
AnyValue::UInt16(v) => Json::from(*v as u64),
AnyValue::UInt32(v) => Json::from(*v as u64),
AnyValue::UInt64(v) => Json::from(*v),
AnyValue::Float32(v) => json_f64(*v as f64),
AnyValue::Float64(v) => json_f64(*v),
AnyValue::String(s) => Json::String(s.to_string()),
AnyValue::StringOwned(s) => Json::String(s.to_string()),
AnyValue::Date(days) => Json::String(days_to_iso(*days)),
other => Json::String(format!("{other}")),
}
}
fn json_f64(f: f64) -> Json {
serde_json::Number::from_f64(f)
.map(Json::Number)
.unwrap_or(Json::Null)
}
pub fn av_to_f64(av: &AnyValue) -> Option<f64> {
match av {
AnyValue::Int8(v) => Some(*v as f64),
AnyValue::Int16(v) => Some(*v as f64),
AnyValue::Int32(v) => Some(*v as f64),
AnyValue::Int64(v) => Some(*v as f64),
AnyValue::UInt8(v) => Some(*v as f64),
AnyValue::UInt16(v) => Some(*v as f64),
AnyValue::UInt32(v) => Some(*v as f64),
AnyValue::UInt64(v) => Some(*v as f64),
AnyValue::Float32(v) => Some(*v as f64),
AnyValue::Float64(v) => Some(*v),
AnyValue::Boolean(b) => Some(if *b { 1.0 } else { 0.0 }),
_ => None,
}
}
pub fn av_to_label(av: &AnyValue) -> String {
match av {
AnyValue::Null => "None".into(),
AnyValue::Boolean(b) => {
if *b {
"True".into()
} else {
"False".into()
}
}
AnyValue::String(s) => s.to_string(),
AnyValue::StringOwned(s) => s.to_string(),
AnyValue::Date(days) => days_to_iso(*days),
other => format!("{other}"),
}
}
pub fn fingerprint(key: &[AnyValue]) -> String {
let mut s = String::new();
for av in key {
match av {
AnyValue::Null => s.push('N'),
AnyValue::Boolean(b) => {
s.push('b');
s.push(if *b { 'T' } else { 'F' });
}
AnyValue::String(v) => {
s.push('s');
s.push_str(v);
}
AnyValue::StringOwned(v) => {
s.push('s');
s.push_str(v);
}
other => {
s.push('x');
s.push_str(&format!("{other}"));
}
}
s.push('\u{1}');
}
s
}