Nivethan
Posted on November 9, 2020
Now back to our regularly scheduled content.
Hello! Chapter 2 was completely optional so we don't really have anything to recap. This chapter we will get Rust set up and get the smallest kernel of our client working.
A note is that we are going to be writing all our code in one file, src/main.rs. This is because I find it easier to hold everything in my head if it is all in one file. Once you have a handle on the concepts, you can split the code into logical files. In my eyes, TLS, and parsing could be easily split up but for now let's stick with one file for everything!
Let's get started!
Installing Rust
Rust, like many other utilities, installs via a shell script that we execute. However you shouldn't trust every shell script execute command on the internet so let's go directly to the rust site to install rust.
https://www.rust-lang.org/tools/install
On that page, you will get a curl command which will get piped to sh which will install rust onto your computer.
Of course, this is probably only true for Linux distributions. I personally use Windows Subsystem for Linux which I really like. The polish of windows with the programming environment of linux.
! Good luck with the installation, sometimes this step is the hardest to get working.
Once we have rust installed, we can verify our installation by printing our version.
> rustc --version
rustc 1.47.0 (18bf6b4f0 2020-10-07)
IDE
Unfortunately I don't have any knowledge about GUI IDEs for Rust. You may want to search for a rust IDE and/or look into using VSCode as it may have a plugin for the rust language server.
The language server is what allows an IDE to tell us all the errors we're making before we compile. Extremely valuable! Especially in the case of using rust via the Windows Subsystem for Linux as compile times are slow on it.
I use vim with YouCompleteMe with the rust language server option. I find that this works really well. I really like vim. (Remember this, you will be tested!)
- Side note - the fish shell is quite fun to use so if you are in the mood to try a new shell, I would recommend giving it a shot.
https://jvns.ca/blog/2017/04/23/the-fish-shell-is-awesome/
Getting Started
Alright! Hopefully you have rust installed and it went smoothly. Let's jump straight into starting out application.
> cargo new gem-client
Created binary (application) `gem-client` package
We're going to create a new rust project using cargo and we've called it gem-client.
- I did it look at the Gemini Wikipedia page to steal a name but I didn't find anything I liked. Closest was 'gusmobile' which is what the NASA guys called the Gemini capsules, named after Gus Grissom.
Now let's make sure everything works!
> cd gem-client/
> cargo run
Compiling gem-client v0.1.0 (/home/nivethan/bp/gem-client)
Finished dev [unoptimized + debuginfo] target(s) in 1.21s
Running `target/debug/gem-client`
Hello, world!
We first cd into our project, and then we will build and run our project with cargo run.
Voila! Our started application has compiled and run. Feel free to take a peek at src/main.rs as that is what we just compiled, and the file we will be editing through out our little adventure.
Making Life Easier
Before we move to writing some code, we have one quality of life improvement that I would say is on par with syntax highlighting - code recompiling on change! (Okay maybe a bit hyperbolic, I can without auto recompiles)
Currently if we write any code, we would have to manually recompile to see our changes. We're going to install cargo-watch and use that to do our recompiling.
> cargo install cargo-watch
Once cargo watch is installed, we can trigger it by the following commands. You will need a shell open and sitting in the correct directory for this step. I have 2 shells open, one shell contains vim, the other contains cargo watch.
/path/to/gem-client
> cargo watch -x run
[Running 'cargo run']
Finished dev [unoptimized + debuginfo] target(s) in 0.03s
Running `target/debug/gem-client`
Hello, world!
[Finished running. Exit status: 0]
Now we can write some code and cargo will immediately recompile and run our code every time we save. Wonderful!
Our Gemini Client
Now, now, now we can write some code, I promise.
We are going to write the kernel of our client and in the following chapters, we'll add so much stuff on top that you'll be surprised that we started so small!
./src/main.rs
use std::io;
use std::io::{Write};
fn main() {
let prompt = ">";
loop {
print!("{} ", prompt);
io::stdout().flush().unwrap();
let mut command = String::new();
io::stdin().read_line(&mut command).unwrap();
println!("{}", command.trim());
}
}
Our Gemini client is going to be a command line program and so we don't want it to exit after just visiting a gemini space, we want to stay in out client as we may have other things to do.
This is why we have an infinite loop going. We begin first by including some crates we will be using. io stands for input/output and is part of rust's standard library, std. We also need to bring in the Write option from the io crate specifically as we will be using it to do the flush().
Next we start our main function. This is the function that will get executed when our binary file is executed.
The first step is to set up some sort of prompt. For now it will just be the angle bracket. Next we start out infinite loop.
Inside our loop, we use the print! macro because we want to keep the cursor on the same line. The println! macro would append a \n to whatever we print so the cursor would be one line below our prompt.
You can test this out by changing print! to println!.
Next we need to flush the buffer, I'm not sure why this is the case, it appears print! simply places data in the output buffer but doesn't write to the screen. It may be \n is a flushing character and because print! doesn't add it, we need to manually flush the output.
Either way! Once we flush it we can then see it on screen.
The next 2 commands are related, this is how we get input from the user. We will read in the standard input and have it write to command. When the user hits enter, the input buffer is processed and we can then move to the final line in our code.
The final line of our little loop simply writes out what the user typed in. We use the trim() function to remove the \n the command came in with.
! There we have it! The very essence of our client. It doesn't do anything yet but we'll get there.
In the next chapter we will look at responding to our users input and doing things based off of it. We'll also be writing the core of our Gemini client in the next chapter - the visit function!
Posted on November 9, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.