tabled 0.16.0

An easy to use library for pretty print tables of Rust `struct`s and `enum`s.
Documentation
//! This example can be run with the following command:
//!
//! `cargo run --example shadow -- "Some text" "In the box"`
//!
//! This example demonstrates using the [`Shadow`] [`TableOption`] to create
//! a striking frame around a [`Table`] display.
//!
//! * [`Shadow`] supports several configurations:
//!     * Thickness
//!     * Offset
//!     * Direction
//!     * Color
//!     * Fill character
//!
//! * 🎉 Inspired by <https://en.wikipedia.org/wiki/Box-drawing_character>

use std::iter::FromIterator;

use tabled::{
    builder::Builder,
    grid::{config::Borders, util::string},
    row,
    settings::{
        style::{LineChar, Style},
        Height, Padding, Shadow, Width,
    },
    Table,
};

fn main() {
    let message = read_message();
    print_table(message);
}

fn print_table(message: String) {
    let main_table = create_main_table(&message);
    let main_table_width = main_table.total_width();
    let small_table_row = create_small_table_list(main_table_width);
    println!("{small_table_row}");
    println!("{main_table}");
}

fn read_message() -> String {
    let args = std::env::args().collect::<Vec<_>>();

    if args.len() < 2 {
        eprintln!("Expected to get at least 1 argument to be printed");
        std::process::exit(-1);
    }

    let mut buf = String::new();
    for (i, text) in args.iter().skip(1).enumerate() {
        if i > 0 {
            buf.push('\n');
        }

        buf.push_str(text);
    }

    buf
}

fn create_small_table_list(width_available: usize) -> String {
    let style1 = Style::modern();
    let style2 = Style::extended();
    let style3 = Style::modern()
        .left('')
        .right('')
        .intersection_left('')
        .intersection_right('')
        .corner_top_right('')
        .corner_top_left('')
        .corner_bottom_right('')
        .corner_bottom_left('');
    let style4 = Style::modern()
        .top('')
        .bottom('')
        .corner_top_right('')
        .corner_top_left('')
        .corner_bottom_right('')
        .corner_bottom_left('')
        .horizontal('')
        .intersection_left('')
        .intersection_right('')
        .intersection_top('')
        .intersection_bottom('')
        .intersection('');

    let mut tables = [
        create_small_table(Borders::from(style1)),
        create_small_table(Borders::from(style2)),
        create_small_table(Borders::from(style3)),
        create_small_table(Borders::from(style4)),
    ];

    const TOTAL_TABLE_WIDTH: usize = 19;

    if width_available > TOTAL_TABLE_WIDTH {
        let mut rest = width_available - TOTAL_TABLE_WIDTH;
        while rest > 0 {
            for table in &mut tables {
                let current_width = table.total_width();
                table.with(Width::increase(current_width + 1));
                rest -= 1;

                if rest == 0 {
                    break;
                }
            }
        }
    }

    let small_table_row = row![tables[0], tables[1], tables[2], tables[3]]
        .with(Style::blank())
        .with(Padding::zero())
        .to_string();
    small_table_row
}

fn create_small_table(style: Borders<char>) -> Table {
    let mut table = Builder::from_iter(vec![vec![" ", ""], vec![" ", ""]]).build();
    table
        .with(style)
        .with(Padding::zero())
        .with(Height::list([1, 0]));

    table
}

// todo: very likely can be simplified
fn create_main_table(message: &str) -> Table {
    let (count_lines, message_width) = string::get_text_dimension(message);
    let count_additional_separators = if count_lines > 2 { count_lines - 2 } else { 0 };
    let left_table_space = (0..count_additional_separators)
        .map(|_| "\n")
        .collect::<String>();

    let left_table = format!(
        "  ╔═══╗ \n  ╚═╦═╝ \n{}═╤══╩══╤\n ├──┬──┤\n └──┴──┘",
        left_table_space
    );

    let mut message = message.to_owned();
    if count_lines < 2 {
        let mut i = count_lines;
        while i < 2 {
            message.push('\n');
            i += 1;
        }
    }

    let count_lines = count_lines.max(2);

    let message = format!("{}\n{}", message, "".repeat(message_width));

    let mut table = row![left_table, message];
    table
        .with(Padding::zero())
        .with(Style::modern().remove_vertical())
        .modify((0, 0), LineChar::vertical('', count_lines))
        .modify((0, 2), LineChar::vertical('', count_lines))
        .with(Shadow::new(2));

    table
}