1 Month Learning Rust - My First CLI Application

1 Month Learning Rust - My First CLI Application

Daily short news for you
  • Privacy Guides is a non-profit project aimed at providing users with insights into privacy rights, while also recommending best practices or tools to help reclaim privacy in the world of the Internet.

    There are many great articles here, and I will take the example of three concepts that are often confused or misrepresented: Privacy, Security, and Anonymity. While many people who oppose privacy argue that a person does not need privacy if they have 'nothing to hide.' 'This is a dangerous misconception, as it creates the impression that those who demand privacy must be deviant, criminal, or wrongdoers.' - Why Privacy Matters.

    » Read more
  • There is a wonderful place to learn, or if you're stuck in the thought that there's nothing left to learn, then the comments over at Hacker News are just for you.

    Y Combinator - the company behind Hacker News focuses on venture capital investments for startups in Silicon Valley, so it’s no surprise that there are many brilliant minds commenting here. But their casual discussions provide us with keywords that can open up many new insights.

    Don't believe it? Just scroll a bit, click on a post that matches your interests, check out the comments, and don’t forget to grab a cup of coffee next to you ☕️

    » Read more
  • Just got played by my buddy Turso. The server suddenly crashed, and checking the logs revealed a lot of errors:

    Operation was blocked LibsqlError: PROXY_ERROR: error executing a request on the primary

    Suspicious, I went to the Turso admin panel and saw the statistics showing that I had executed over 500 million write commands!? At that moment, I was like, "What the heck? Am I being DDoSed? But there's no way I could have written 500 million."

    Turso offers users free monthly limits of 1 billion read requests and 25 million write requests, yet I had written over 500 million. Does that seem unreasonable to everyone? 😆. But the server was down, and should I really spend money to get it back online? Roughly calculating, 500M would cost about $500.

    After that, I went to the Discord channel seeking help, and very quickly someone came in to assist me, and just a few minutes later they informed me that the error was on their side and had restored the service for me. Truly, in the midst of misfortune, there’s good fortune; what I love most about this service is the quick support like this 🙏

    » Read more

The Problem

A year ago, I declared that I would learn Rust within a month. And the result is that the series of articles about the Rust learning process has not ended yet. Can this be considered a failure? I think not.

Programming languages are just tools to solve problems. Learning a new one can broaden our experience and help us solve problems more efficiently. Besides learning, I also have other tasks with higher priority, so I have to work on them first.

Many times, I wanted to give up, but the series of articles about Rust was still well-received by readers. I don't know why, because what I wrote was just a summary or some explanations of my understanding of Rust. It seems that what people want to see is a companion. Ah! At least there is someone learning Rust like me.

In the past year, we have learned most of the basic knowledge of Rust, including variables, data types, and basic data structures. The most difficult part - ownership - I think should be applied to a real project to encounter problems.

So, this is the time to do a practical exercise, applying what we have learned to create a command-line tool (CLI). But then I remembered that I also created a CLI application to create and upload thumbnail images in the article Using CLI to Increase Efficiency in Work.

The previous application solved the problem of processing image sizes and uploading them in bulk to the blog. Imagine you have an image, regardless of the format and size, and when you put it into the CLI, it will create a series of images with different sizes and upload them to R2 of Cloudflare.

But, if we bring all the features of the old CLI to the new one, it will take a lot of time, so in this short article, I will simplify it. I will ignore the image processing step and just upload the image to R2.

Let's start!

First, create a new Rust project using cargo, named img-cli.

$ cargo new img-cli

Run the program.

$ cargo run
   Compiling img-cli v0.1.0 (/Users/hoaitx/src/hoaitx/img-cli)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.35s
     Running `target/debug/img-cli`
Hello, world!

Now, we need to get the argument which is the path to the image to be uploaded. Something like this:

$ img-cli path_to_image

In Rust, the simplest way to get the arguments after the command is args.

fn main() {
    let args: Vec<String> = env::args().collect();
    let file_path = &args[1];
}

The API to upload the image to R2 uses a PUT request, with a binary body containing the image data. One thing to note is that the name of the image uploaded to R2 is extracted from the API path. Additionally, a secret parameter 'X-Custom' in the headers is needed to authenticate the upload.

First, write a function to read the image file data.

fn read_file(path: &str) -> Result<Vec<u8>, std::io::Error> {
    let mut file = File::open(path)?;
    let mut file_bytes = Vec::new();
    file.read_to_end(&mut file_bytes)?;
    Ok(file_bytes)
}

read_file takes a path to the image, returns the data as Vec<u8> if successful, or an error.

Next, write a function to upload the image to R2. I use the reqwest library to call the API. This library usually comes with tokio to handle asynchronous operations. For simplicity, we will use the blocking API of reqwest.

Install reqwest:

$ cargo add reqwest --features blocking

After calling the API, the result is a JSON containing the link to the uploaded image. We need to extract this link and print it out. To do this, we need to map the response data to a struct. Install serde and serde_json to handle JSON.

$ cargo add serde --features derive
$ cargo add serde_json

Declare a struct Response to map the API response.

#[derive(serde::Deserialize, serde::Serialize)]
struct Response {
    full: String,
}

Put everything together into a function up_file.

#[derive(serde::Deserialize, serde::Serialize)]
struct Response {
    full: String,
}

fn up_file(path: &str) -> Result<String, Box<dyn std::error::Error>> {
    let url = "https://static-img.2coffee.dev";
    let client = reqwest::blocking::Client::new();
    let file_path = path;

    // get the file name from the path
    let file_name = file_path
        .split('/')
        .last()
        .unwrap_or("unknown")
        .to_string();

    let file_bytes = read_file(file_path)?;

    let response = client
        .put(format!("{}/{}", url, file_name))
        .header("Content-Type", "application/x-binary")
        .header("X-Custom-Auth-Key", "XXX")
        .body(file_bytes)
        .send();

    let response_struct: Response =
        serde_json::from_str(response?.text()?.as_str())?;

    Ok(response_struct.full)
}

Put the up_file function into main.

fn main() {
    let args: Vec<String> = env::args().collect();
    let file_path = &args[1];
    let url = up_file(file_path);

    print!("{}", url.unwrap());
}

Run the program.

$ cargo run -- '/Users/hoaitx/Downloads/hoaitx.jpg'
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.20s
     Running `target/debug/rs /Users/hoaitx/Downloads/hoaitx.jpg`
https://static-img.2coffee.dev/hoaitx.jpg

It works!

In this article, we applied basic knowledge and used some libraries to call the API and handle the response. It's not easy when we start working on a real project. In future articles, we will move on to more advanced concepts in Rust.

Premium
Hello

5 profound lessons

Every product comes with stories. The success of others is an inspiration for many to follow. 5 lessons learned have changed me forever. How about you? Click now!

Every product comes with stories. The success of others is an inspiration for many to follow. 5 lessons learned have changed me forever. How about you? Click now!

View all

Subscribe to receive new article notifications

or
* The summary newsletter is sent every 1-2 weeks, cancel anytime.

Comments (0)

Leave a comment...