use unicode_width::UnicodeWidthStr;
pub const COLUMN_GUTTER: usize = 2;
pub fn grid_columns(available_width: usize, cell_width: usize, item_count: usize) -> usize {
if item_count == 0 {
return 1;
}
let col_width = cell_width + COLUMN_GUTTER;
let columns = available_width.checked_div(col_width).unwrap_or(item_count).max(1);
columns.min(item_count)
}
pub fn grid_position(index: usize, rows: usize) -> (usize, usize) {
let rows = rows.max(1);
(index % rows, index / rows)
}
pub fn index_from_position(row: usize, col: usize, rows: usize) -> usize {
col * rows.max(1) + row
}
pub fn display_width(s: &str) -> usize {
UnicodeWidthStr::width(s)
}
pub fn rows_needed(item_count: usize, columns: usize) -> usize {
if item_count == 0 {
return 0;
}
item_count.div_ceil(columns.max(1))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn columns_never_zero() {
assert_eq!(grid_columns(0, 10, 5), 1);
assert_eq!(grid_columns(80, 0, 5), 5);
}
#[test]
fn columns_fit_width() {
assert_eq!(grid_columns(40, 10, 100), 3);
}
#[test]
fn columns_capped_by_item_count() {
assert_eq!(grid_columns(200, 5, 2), 2);
}
#[test]
fn position_round_trips() {
let rows = 4;
for index in 0..17 {
let (row, col) = grid_position(index, rows);
assert_eq!(index_from_position(row, col, rows), index);
}
}
#[test]
fn fills_top_to_bottom_within_a_column_before_the_next_column() {
let rows = 3;
assert_eq!(grid_position(0, rows), (0, 0));
assert_eq!(grid_position(1, rows), (1, 0));
assert_eq!(grid_position(2, rows), (2, 0));
assert_eq!(grid_position(3, rows), (0, 1));
assert_eq!(grid_position(4, rows), (1, 1));
assert_eq!(grid_position(6, rows), (0, 2));
}
#[test]
fn rows_needed_ceils() {
assert_eq!(rows_needed(10, 3), 4);
assert_eq!(rows_needed(9, 3), 3);
assert_eq!(rows_needed(0, 3), 0);
}
}