DEV Community

Cover image for Making an md file reader using RUST
Damon Marc Rocha II
Damon Marc Rocha II

Posted on • Edited on

Making an md file reader using RUST

I recently ran across a tutorial explaining how to create an md file reader in Rust, using only three dependencies!! I was so excited about this project that I decided to share it with all of you.

So after creating a new rust file with

cargo new app-name
Enter fullscreen mode Exit fullscreen mode

You will need to add clap to your project's cargo.toml file under dependencies. Along with this go ahead and add pulldown-cmark and maud as shown below:

[dependencies]
clap ="2.33.0"
pulldown-cmark = "0.4.1"
maud = "0.20.0"
Enter fullscreen mode Exit fullscreen mode

At this point, I know there are some questions, and I will answer them to the best of my ability. So to cover the first dependency, clap is a library that allows one to parse command-line arguments and subcommands when writing console/terminal applications. This crate allows you to create custom arguments that you can add to the file run command to output various items. It can also format the display information on help queries as shown below:

Alt Text

To get this run

cargo run -- --help

which will output the file's help information. If you look towards the top you can see my name, the crate version, and a simple description; that could literally be ANYTHING I WANT. Along with these customizations, you can add different arguments to run with the file as shown in the help flags above and in my code below:

nameOfApp =>
                            (version:crate_version!()) 
                            (author:"Damon Rocha")
                            (about:"Renders markdown as you like")
                            (@arg input: + required "Sets the input file")  
                            (@arg wrap: -w "Wrap in html")
                            (@arg event: -e "Print event")
                            (@arg css: --css + takes_value "Link to css")   
Enter fullscreen mode Exit fullscreen mode

So what does all of this mean? Well, the input arg allows the user to set the file to be read. The event arg will print out the events as the items are parsed to HTML, and the CSS arg generates a link to a CSS file you input with the file on execution.

Next is pulldown-cmark. From this library, I imported html::push_html, Event, and Parser. These three functions allowed me to take the data read from a file, Parse through it, collect the Events, and then convert the file data into HTML. To accomplish this, first read in the md file the user inputs on runtime

cargo run --filename.md
Enter fullscreen mode Exit fullscreen mode

After doing this the app will save the file data to a variable as a string, and
then create a Parser to hold the HTML and a Vec to hold the file data along with the event information.

 let infile = 
    std::fs::read_to_string(clap.value_of("input").unwrap())
    .expect("Could not read file");
 let mut res = String::new();

 let ps = Parser::new(&infile);
 let ps : Vec<Event> = ps.into_iter().collect();
...
 push_html(&mut res, ps.into_iter());
Enter fullscreen mode Exit fullscreen mode

With all of the HTML from the file contained in the res String, multiple choices are available. First just running the file will display the html present in the file. Running the file along with event will display data like what is below

Input = Some("test_data/hello.md")
Start(Header(1))
Text(Borrowed("Hello"))
End(Header(1))
Start(Paragraph)
Text(Borrowed("Here is a page of markdown "))
Start(Emphasis)
Text(Borrowed("stuff"))
End(Emphasis)
End(Paragraph)
<h1>Hello</h1>
<p>Here is a page of markdown <em>stuff</em></p>

Enter fullscreen mode Exit fullscreen mode

Lastly, there is the wrap and CSS choice. This is where maud comes in, but don't go running cargo run just yet. First, you need a different version of Rust. So to get maud to work first download Nightly rust, which is a Rust distribution channel where some files are still in the beta stages. If you run into any problems with maud just let those at Rust know. This has not happened to me yet but you never know. Download nightly rust with the command

rustup install nightly
Enter fullscreen mode Exit fullscreen mode

then set this file to run nightly by default with

rustup override set nightly
Enter fullscreen mode Exit fullscreen mode

once this is done you can then use maud. maud is a crate that allows you to wrap your read in HTML in different HTML tags. In my project, I just used a basic DOCTYPE, head, and body tag to hold the information from the file. To do this I created a function called wrap_html that is shown below:

 fn wrap_html(s:&str, css:Option<&str>)-> String{
    let res = html!{
        (maud::DOCTYPE)
        html{
            head{
                meta charset="utf-8";
                @if let Some(s) = css {
                    link rel="stylesheet" type="text/css" href=(s) {}
                }
            }
            body{
                (maud::PreEscaped(s))
            }
        }
    };
    res.into_string()
}
Enter fullscreen mode Exit fullscreen mode

While using maud to access your variables you must wrap them in () and any control flow needs to be paired with an @ before it as shown above. I set CSS to an Option type so that if a user does not send any CSS to the HTML wrapper the CSS link will not be present. Then once in the body the HTML sent in is put into the wrapper with maud::PreEscaped(s).
With this done all that is left is adding control flow to make those choices mean something. To do this I added two if blocks in the main function to check if certain arguments are present in the clap block

 if clap.is_present("event"){

        for p in &ps{
            println!("{:?}", p);
        }
    }//prints out events along with html

 push_html(&mut res, ps.into_iter());
 if clap.is_present("wrap"){
        res = wrap_html(&res, clap.value_of("css"));
    }//prints out wrapped html
...
Enter fullscreen mode Exit fullscreen mode

Then to actually display something we must print out all of the data that has been accumulated.

  println!("{}", res);
Enter fullscreen mode Exit fullscreen mode

You can now get the HTML from an md file in your terminal. So if you enjoyed this project then make changes and improve it. And please send me a link to your project. I know I am going to tinker with this until it is a project I can really be proud of. Repo: https://github.com/dmarcr1997/RustfileParser

Top comments (5)

Collapse
 
arcticspacefox profile image
ArcticSpaceFox

Thank you, more content

Collapse
 
dmarcr1997 profile image
Damon Marc Rocha II

Your welcome. I will post some more rust stuff next week. Anything in particular?

Collapse
 
arcticspacefox profile image
ArcticSpaceFox

Maybe a simple Machine Learning project, not Google deepmind but a linear regression problem with python bindings? Thanks though for posting Rust content!

Thread Thread
 
dmarcr1997 profile image
Damon Marc Rocha II

I will try to do this, but I do not have a lot of experience using machine learning in python. I have actually wanted to do something similar to this with JS or React. But it will be a challenge to use python so let me see what I can do by next Sunday. Yeah your welcome, I find it cements what I am learning if I write about it afterward and Rust is awesome.

Thread Thread
 
arcticspacefox profile image
ArcticSpaceFox

Hey js is fine by me 😄✌️ just a high level language 👍