loading...

Drawing tables in console using Rust

elasticrash profile image Stefanos Kouroupis ・3 min read

I keep finding myself, coming back and thinking...Rust is not that popular, not that many people are using/reading about Rust.

Maybe I should write something about Javascript. At the end of the day I owe Javascript 80% of my traffic (over 15k view) while Rust ...has around 300 views. One could argue that I haven't written much about Rust or that my articles/examples (I am not kidding myself most things I write are kind of examples/tutorials...maybe) are a bit pointless.

And that's the thing. In my free time I don't feel like being productive. I love pointless stupid things. Like this one.

I created a small app....which takes absolutely no advantage whatsoever of any feature that rust has to offer. But I am having fun.

And this app..🥁..draws tables in a console using utf characters 😞. This article though is less than an example but rather a journey down the memory lane.

So you get this nice output

A

B

C

You could think of those projects as an 1 hour challenge. And I do try to do things as much as properly as my patience allows me, outside work.


Our glorious imports. I am only using a single external dependency in this project and the reason why, is simple. Rust has no build in functionality to return the width and height of the console (in characters). If someone prefers not to use an external dependency, they have to write that functionality in C and wrap it up for Rust.

extern crate term_size;
use std::io::{self};
use std::process::exit;
use std::str::{self};

One of the reasons I did this exercise was partly sentimental. I grew up in late 80s/early 90s and I remember my mother writing documents using a computer. She was using a software called Volkswriter which was a document editor which bears no resemblance to modern document editors (like Word). It was, and still is, in my mind the vim or document editors.

So quite frequently my mum had to draft a table. The way you "insert" a table was by using characters. So basically you had to "type" a table. Modifying a table was a pain in the $%^&!

Here is the list of characters that were quite popular back then (along with double line variations.

fn main() {
    let characters: Vec<&str> = vec![
        "━", "┏", "┓", "┗", "┛", "┃", " ", "┠", "┨", "┯", "┷", "│", "─", "┼",
    ];

Being 12 at the time, I tried to automate the process using BASIC and failed miserably and then went back to keep playing golden axe. So now ~30 years later I came back and finished the unfinished job.

The app takes a pair of numbers as an input (no of cells to divide horizontally and vertically) and types the desired table to fit all the available console.

    match term_size::dimensions() {
        Some((w, h)) => {
            println!("divide horizontally (max value {})", w / 4);
            let hor_div = get_value();
            if hor_div > ((w as u32) / 4) {
                println!("invalid range, terminating application");
                exit(0);
            }

            println!("divide vertically (max value {})", h / 4);
            let vert_div = get_value();
            if vert_div > ((h as u32) / 4) {
                println!("invalid range, terminating application");
                exit(0);
            }
            let every = h as u32/vert_div;

            for j in 0..h {
                if j == 0 {
                    output(
                        characters[1],
                        characters[0],
                        characters[2],
                        w,
                        characters[9],
                        hor_div,
                    );
                } else if j == h - 1 {
                    output(
                        characters[3],
                        characters[0],
                        characters[4],
                        w,
                        characters[10],
                        hor_div,
                    );
                } else {
                    if j as u32 % every != 0 {
                        output(
                            characters[5],
                            characters[6],
                            characters[5],
                            w,
                            characters[11],
                            hor_div,
                        );
                    } else {
                        output(
                            characters[7],
                            characters[12],
                            characters[8],
                            w,
                            characters[13],
                            hor_div,
                        );
                    }
                }
            }
        }
        _ => {}
    }
}

pub fn output(a: &str, b: &str, c: &str, w: usize, d: &str, div: u32) {
    print!("{}", a);
    let every = w as u32/div;
    for _i in 1..w {
        if _i as u32 % every == 0 {
            print!("{}", d);
        } else {
            print!("{}", b);
        }
    }
    print!("{}", c);
}

pub fn get_value() -> u32 {
    let mut dim = String::new();
    match io::stdin().read_line(&mut dim) {
        Err(why) => panic!("couldn't read {:?}", why.raw_os_error()),
        _ => (),
    };

    let value = match dim.trim().parse::<u32>() {
        Ok(v) => v,
        Err(why) => panic!("Invalid input ...exiting due to {:?}", why),
    };

    return value;
}

Discussion

pic
Editor guide