Masai Mahapa

30 days of Rust - Day Twelve - Project: Predict My Country

30 days of rust - day twelve

Success has to do with deliberate practice. Practice must be focused, determined and in an environment where there's feedback. - Malcolm Gladwell Today, I decided to build a small project. Reading about swimming all day will not make you a great swimmer 🏊🏼. You need to jump into the deep end. This is exactly what I decided to do today.

I built a command line application which predicts which country you are from by using your first name. Here is a link to the project on my Github.

name-predictor

Day 12 - Project : Guess My Country

In this application I implemented;

  1. Command Line Arguments
  2. Async / Await (new)
  3. Converting text to json

Let's go into each of these.

Command line arguments

In order for the program to allow the user to enter their name, I needed a way to take it in via the command line. This is actually quite easy in Rust;

use std::env;
 
let args: Vec<String> = env::args().collect();

We firstly bring env into scope from the standard library. Then we can call env::args().collect() which gives us a list of arguments in the command line, including the program name.

So now if we input;

cargo run masai

The variable args will now hold;

["target/debug/cli", "masai"]

We can then access the element with index 1 and use it within our program. This can be as many arguments as we want by the way.

Async / Await

So for this application to work, I need to call an API called Nationalize.io and pass it the name in order to get the predictions.

Now, calling that API will take some time since we need to wait for the response. Let's say it takes 2 seconds, we need our program to sit and wait for that response so we can tell the user which country they're most likely from.

So now I created the function fetch_data to do just that. The async keyword states that this function is asynchronous.;

pub async fn fetch_data(name : &str) -> Result<String, Box<dyn error::Error>>{

The code below waits for the result from fetch_data to return before trying to do anything with it. In the case that it fails, we get an error saying could not fetch the data.

&result.await.expect("could not fetch the data")

I don't yet fully understand this concept and I will have a proper blog post on it.

converting text to json

I used the crate called Serde in order to turn the text I got from the api call into json.

pub fn turn_into_json(text : &str) -> serde_json::Result<NamePredictions> {
   let name_predictions: NamePredictions = serde_json::from_str(text)?;
   Ok(name_predictions)
}

Error Handling

If a user does not pass in a name when running this program, it will show a user friendly message instead of a panic. The code below handles that;

let args: Vec<String> = env::args().collect();
let name: &str;
if args.len() >1 {
   name = &args[1];
} else {
   eprintln!("Please enter a name. e.g cargo run James");
   process::exit(1);
}

The output of running the program without a name is; name-predictor

To log the error, I use eprintln! macro instead of println. This is the other type of output I just learned of today, which should be used for error messages. It doesn't really do much different here, but it would allow us for example to write the predictions to a file and show only the errors on the console. It's just nice to know for now.

Conclusion

The above program taught me alot in a short space of time. I learnt how to take in dynamic user input from the terminal, how to make requests over the internet, convert text to json format as well as different output streams.

The program can be improved in many ways I am sure, but I feel like this is a good starting point.

Please let me know what you think of this series and also ideas you think I should try to build.

View the full code on Github. Thanks for spending some time learning Rust with me. Until next time. Peace ✌🏼 .

Share