278 lines
9.1 KiB
Rust
278 lines
9.1 KiB
Rust
//! High-level format functions for each q value shape.
|
|
//!
|
|
//! Each function produces a multi-line ASCII repr string. Rendering is driven
|
|
//! by the active [`FormattingOptions`] (read from the process-wide global).
|
|
|
|
use qroissant_core::Atom as CoreAtom;
|
|
use qroissant_core::Dictionary as CoreDictionary;
|
|
use qroissant_core::List as CoreList;
|
|
use qroissant_core::Table as CoreTable;
|
|
use qroissant_core::Value as CoreValue;
|
|
use qroissant_core::Vector as CoreVector;
|
|
use qroissant_core::VectorData;
|
|
|
|
use super::cell::atom_primitive_label;
|
|
use super::cell::format_atom_cell;
|
|
use super::cell::format_atom_raw;
|
|
use super::cell::format_char_vector;
|
|
use super::cell::format_vector_item;
|
|
use super::cell::primitive_label;
|
|
use super::cell::truncate;
|
|
use super::options::active_options;
|
|
use super::render::PreviewSlot;
|
|
use super::render::preview_slots;
|
|
use super::render::render_preview;
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Attribute helper
|
|
// ---------------------------------------------------------------------------
|
|
|
|
fn attribute_label(attribute: qroissant_core::Attribute) -> &'static str {
|
|
match attribute {
|
|
qroissant_core::Attribute::None => "none",
|
|
qroissant_core::Attribute::Sorted => "sorted",
|
|
qroissant_core::Attribute::Unique => "unique",
|
|
qroissant_core::Attribute::Parted => "parted",
|
|
qroissant_core::Attribute::Grouped => "grouped",
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Atom
|
|
// ---------------------------------------------------------------------------
|
|
|
|
pub fn format_atom(atom: &CoreAtom) -> String {
|
|
let label = atom_primitive_label(atom);
|
|
render_preview(
|
|
vec![format!("Atom [{label}]")],
|
|
vec!["value".to_string()],
|
|
vec![vec![format_atom_cell(atom)]],
|
|
vec!["shape: (1,)".to_string()],
|
|
)
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Vector
|
|
// ---------------------------------------------------------------------------
|
|
|
|
pub fn format_vector(vector: &CoreVector) -> String {
|
|
let len = vector.len();
|
|
let data = vector.data();
|
|
let label = primitive_label(data);
|
|
let attr = vector.attribute();
|
|
|
|
let rows = match data {
|
|
VectorData::Char(chars) => {
|
|
vec![vec![format_char_vector(chars)]]
|
|
}
|
|
_ => {
|
|
let opts = active_options();
|
|
preview_slots(len, opts.max_rows, opts.row_display)
|
|
.into_iter()
|
|
.map(|slot| match slot {
|
|
PreviewSlot::Index(i) => vec![format_vector_item(data, i)],
|
|
PreviewSlot::Ellipsis => vec!["...".to_string()],
|
|
})
|
|
.collect()
|
|
}
|
|
};
|
|
|
|
render_preview(
|
|
vec![format!("Vector [{label}, attr={}]", attribute_label(attr))],
|
|
vec!["value".to_string()],
|
|
rows,
|
|
vec![format!("shape: ({len},)")],
|
|
)
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// List
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/// Compact single-line summary of any `CoreValue` (used for list/dict cells).
|
|
fn inline_value_summary(value: &CoreValue) -> String {
|
|
match value {
|
|
CoreValue::Atom(atom) => truncate(format!(
|
|
"{} [{}]",
|
|
format_atom_raw(atom),
|
|
atom_primitive_label(atom)
|
|
)),
|
|
CoreValue::Vector(vector) => {
|
|
let label = primitive_label(vector.data());
|
|
let len = vector.len();
|
|
match vector.data() {
|
|
VectorData::Char(chars) => truncate(format_char_vector(chars)),
|
|
_ => truncate(format!("vector<{label}>[{len}]")),
|
|
}
|
|
}
|
|
CoreValue::List(list) => truncate(format!("list[{}]", list.len())),
|
|
CoreValue::Dictionary(dict) => truncate(format!("dict[{}]", dict.len())),
|
|
CoreValue::Table(table) => {
|
|
truncate(format!("table[{}x{}]", table.len(), table.num_columns()))
|
|
}
|
|
CoreValue::UnaryPrimitive { opcode } => truncate(format!("unary(0x{opcode:02x})")),
|
|
}
|
|
}
|
|
|
|
pub fn format_list(list: &CoreList) -> String {
|
|
let len = list.len();
|
|
let opts = active_options();
|
|
let attr = list.attribute();
|
|
|
|
let rows = preview_slots(len, opts.max_rows, opts.row_display)
|
|
.into_iter()
|
|
.map(|slot| match slot {
|
|
PreviewSlot::Index(i) => vec![inline_value_summary(&list.values()[i])],
|
|
PreviewSlot::Ellipsis => vec!["...".to_string()],
|
|
})
|
|
.collect();
|
|
|
|
render_preview(
|
|
vec![format!("List [list, attr={}]", attribute_label(attr))],
|
|
vec!["value".to_string()],
|
|
rows,
|
|
vec![format!("shape: ({len},)")],
|
|
)
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Dictionary
|
|
// ---------------------------------------------------------------------------
|
|
|
|
pub fn format_dictionary(dict: &CoreDictionary) -> String {
|
|
let size = dict.len();
|
|
let sorted = dict.sorted();
|
|
|
|
let all_rows = vec![
|
|
vec!["keys".to_string(), inline_value_summary(dict.keys())],
|
|
vec!["values".to_string(), inline_value_summary(dict.values())],
|
|
];
|
|
|
|
let opts = active_options();
|
|
let rows = preview_slots(all_rows.len(), opts.max_rows, opts.row_display)
|
|
.into_iter()
|
|
.map(|slot| match slot {
|
|
PreviewSlot::Index(i) => all_rows[i].clone(),
|
|
PreviewSlot::Ellipsis => vec!["...".to_string(), "...".to_string()],
|
|
})
|
|
.collect();
|
|
|
|
render_preview(
|
|
vec![format!("Dictionary [dict, sorted={sorted}]")],
|
|
vec!["part".to_string(), "value".to_string()],
|
|
rows,
|
|
vec![format!("shape: ({size},)")],
|
|
)
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Table
|
|
// ---------------------------------------------------------------------------
|
|
|
|
fn column_primitive_label(col: &CoreValue) -> &'static str {
|
|
match col {
|
|
CoreValue::Vector(v) => primitive_label(v.data()),
|
|
CoreValue::List(_) => "list",
|
|
CoreValue::Atom(_) => "atom",
|
|
_ => "?",
|
|
}
|
|
}
|
|
|
|
fn table_cell(col: &CoreValue, row_index: usize) -> String {
|
|
match col {
|
|
CoreValue::Vector(v) => match v.data() {
|
|
VectorData::Char(chars) => {
|
|
// Show a single char per cell
|
|
if row_index < chars.len() {
|
|
(chars[row_index] as char).to_string()
|
|
} else {
|
|
"?".to_string()
|
|
}
|
|
}
|
|
data => format_vector_item(data, row_index),
|
|
},
|
|
CoreValue::Atom(atom) => format_atom_cell(atom),
|
|
CoreValue::List(list) => {
|
|
if row_index < list.len() {
|
|
inline_value_summary(&list.values()[row_index])
|
|
} else {
|
|
"?".to_string()
|
|
}
|
|
}
|
|
_ => inline_value_summary(col),
|
|
}
|
|
}
|
|
|
|
fn column_name(raw: &[u8]) -> String {
|
|
String::from_utf8_lossy(raw).into_owned()
|
|
}
|
|
|
|
pub fn format_table(table: &CoreTable) -> String {
|
|
let num_rows = table.len();
|
|
let num_cols = table.num_columns();
|
|
let opts = active_options();
|
|
let visible_cols = num_cols.min(opts.max_columns);
|
|
|
|
// Build headers: "name\ntype" for each visible column
|
|
let mut headers: Vec<String> = table
|
|
.column_names()
|
|
.iter()
|
|
.zip(table.columns().iter())
|
|
.take(visible_cols)
|
|
.map(|(name, col)| {
|
|
let col_name = truncate(column_name(name));
|
|
let type_label = column_primitive_label(col);
|
|
format!("{col_name}\n{type_label}")
|
|
})
|
|
.collect();
|
|
|
|
if num_cols > visible_cols {
|
|
headers.push("...\n...".to_string());
|
|
} else if headers.is_empty() {
|
|
headers.push("value".to_string());
|
|
}
|
|
|
|
// Build rows
|
|
let row_slots = preview_slots(num_rows, opts.max_rows, opts.row_display);
|
|
let columns = table.columns();
|
|
|
|
let body_rows: Vec<Vec<String>> = row_slots
|
|
.into_iter()
|
|
.map(|slot| {
|
|
let mut row: Vec<String> = match slot {
|
|
PreviewSlot::Index(row_i) => (0..visible_cols)
|
|
.map(|col_i| table_cell(&columns[col_i], row_i))
|
|
.collect(),
|
|
PreviewSlot::Ellipsis => vec!["...".to_string(); visible_cols.max(1)],
|
|
};
|
|
if num_cols > visible_cols {
|
|
row.push("...".to_string());
|
|
}
|
|
row
|
|
})
|
|
.collect();
|
|
|
|
render_preview(
|
|
vec![format!(
|
|
"Table [table, attr={}]",
|
|
attribute_label(table.attribute())
|
|
)],
|
|
headers,
|
|
body_rows,
|
|
vec![format!("shape: ({num_rows}, {num_cols})")],
|
|
)
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// UnaryPrimitive
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#[allow(dead_code)]
|
|
pub fn format_unary_primitive(opcode: i8) -> String {
|
|
render_preview(
|
|
vec!["UnaryPrimitive [unary_primitive]".to_string()],
|
|
vec!["opcode".to_string()],
|
|
vec![vec![format!("0x{opcode:02x}")]],
|
|
vec!["shape: (1,)".to_string()],
|
|
)
|
|
}
|