///|
/// A reusable option that changes table records, layout, or configuration.
pub(open) trait TableOption {
fn change(Self, Table) -> Unit
fn hint_change(Self) -> Entity? = _
}
///|
impl TableOption with fn hint_change(_self) {
Some(Entity::Global)
}
///|
/// A reusable option applied to a cell, row, column, or the entire table.
pub(open) trait CellOption {
fn change(Self, Table, Entity) -> Unit
fn hint_change(Self) -> Entity? = _
}
///|
impl CellOption with fn hint_change(_self) {
Some(Entity::Global)
}
///|
/// Apply any table option. The suffix avoids MoonBit's `with` keyword.
///
/// ```mbt check
/// test {
/// let table = @tabular.Table::from_rows([["a", "b"]])
/// table.with_(@tabular.Rotate::Left) |> ignore
/// assert_eq(table.rows, [["b"], ["a"]])
/// }
/// ```
pub fn[T : TableOption] Table::with_(self : Table, option : T) -> Table {
let hint = TableOption::hint_change(option)
self.option_depth += 1
TableOption::change(option, self)
self.option_depth -= 1
self.dimension.invalidate(hint)
self
}
///|
/// Apply a cell option to each entity selected by an object.
pub fn[O : Object, M : CellOption] Table::modify(
self : Table,
object : O,
option : M,
) -> Table {
self.option_depth += 1
for entity in Object::entities(object, self) {
CellOption::change(option, self, entity)
}
self.option_depth -= 1
self.dimension.invalidate(CellOption::hint_change(option))
self
}
///|
pub impl CellOption for Setting with fn change(self, table, entity) {
table.option_depth += 1
match entity {
Global => table.modify_segment(Segment::all(), self) |> ignore
Row(row) => table.modify_rows(Rows::one(row), self) |> ignore
Col(col) => table.modify_cols(Cols::one(col), self) |> ignore
Cell(row, col) => table.modify_cell(Cell::new(row, col), self) |> ignore
}
table.option_depth -= 1
}
///|
pub impl TableOption for Setting with fn change(self, table) {
CellOption::change(self, table, Entity::Global)
}
///|
pub impl CellOption for String with fn change(self, table, entity) {
CellOption::change(Setting::format(Format::value(self)), table, entity)
}
///|
pub impl[T : TableOption] TableOption for Array[T] with fn change(self, table) {
for option in self {
TableOption::change(option, table)
}
}
///|
pub impl[T : CellOption] CellOption for Array[T] with fn change(
self,
table,
entity,
) {
for option in self {
CellOption::change(option, table, entity)
}
}
///|
fn combine_hints(hints : Array[Entity?]) -> Entity? {
hints.fold(init=None, (result, next) => {
match (result, next) {
(None, hint) | (hint, None) => hint
(Some(first), Some(second)) => Some(combine_entity(first, second))
}
})
}
///|
fn combine_entity(first : Entity, second : Entity) -> Entity {
match (first, second) {
(Col(a), Col(b)) if a == b => Col(a)
(Col(a), Cell(_, b)) if a == b => Col(a)
(Cell(_, a), Col(b)) if a == b => Col(a)
(Row(a), Row(b)) if a == b => Row(a)
(Row(a), Cell(b, _)) if a == b => Row(a)
(Cell(a, _), Row(b)) if a == b => Row(a)
(Cell(a, b), Cell(c, d)) if a == c && b == d => Cell(a, b)
_ => Global
}
}
///|
pub extend Setting with CellOption::{
change as change_cell,
hint_change as hint_change_cell,
}
///|
pub extend Setting with TableOption::{change, hint_change}