use crate::data::dataframe::DataFrame;
use polars::prelude::AnyValue;
use serde_json::{json, Map, Value};
pub const DEFAULT_LIMIT: usize = 100;
pub const MAX_BYTES: usize = 256 * 1024;
fn cell(value: &AnyValue) -> Value {
match value {
AnyValue::Null => Value::Null,
AnyValue::Boolean(b) => json!(b),
AnyValue::Int8(i) => json!(i),
AnyValue::Int16(i) => json!(i),
AnyValue::Int32(i) => json!(i),
AnyValue::Int64(i) => json!(i),
AnyValue::UInt8(i) => json!(i),
AnyValue::UInt16(i) => json!(i),
AnyValue::UInt32(i) => json!(i),
AnyValue::UInt64(i) => json!(i),
AnyValue::Float32(f) => json!(f.is_finite().then_some(*f)),
AnyValue::Float64(f) => json!(f.is_finite().then_some(*f)),
AnyValue::String(s) => json!(s),
AnyValue::StringOwned(s) => json!(s.as_str()),
other => json!(DataFrame::anyvalue_to_string_fmt(other)),
}
}
pub fn columns_json(df: &DataFrame) -> Vec<Value> {
df.columns
.iter()
.map(|c| json!({"name": c.name, "type": c.col_type.name()}))
.collect()
}
fn rows_json(df: &DataFrame, limit: usize) -> Result<(Vec<Value>, bool), String> {
let visible = df.get_visible_df()?;
let height = visible.height();
let series: Vec<_> = visible.columns().iter().collect();
let mut rows = Vec::new();
let mut bytes = 0usize;
for r in 0..height.min(limit) {
let row: Vec<Value> = series
.iter()
.map(|s| cell(&s.get(r).unwrap_or(AnyValue::Null)))
.collect();
bytes += serde_json::to_string(&row).map(|s| s.len()).unwrap_or(0);
rows.push(Value::Array(row));
if bytes >= MAX_BYTES {
break;
}
}
let truncated = rows.len() < height;
Ok((rows, truncated))
}
pub fn table(df: &DataFrame, limit: usize) -> Result<Value, String> {
let (rows, truncated) = rows_json(df, limit)?;
let row_count = df.row_order.len();
let mut out = Map::new();
out.insert("columns".into(), Value::Array(columns_json(df)));
out.insert("rows".into(), Value::Array(rows.clone()));
out.insert("row_count".into(), json!(row_count));
out.insert("returned".into(), json!(rows.len()));
out.insert("truncated".into(), json!(truncated));
if truncated {
out.insert(
"note".into(),
json!(format!(
"Showing {} of {} rows. Raise 'output.limit', narrow the pipeline, \
or set 'output.path' to write the full result to a file.",
rows.len(),
row_count
)),
);
}
Ok(Value::Object(out))
}