- Published on
Building a Unix-shell in Rust - Part 4
- Authors
-
-
- Name
- David
- @Estikno
-
In the last post, we tackled formatting code, organizing files, handling errors, adding pipes, and using Termion for input. This time, we’ll be discussing the switch from Termion to Rustyline, adding a command history feature, implementing an init script, and creating an installation script. Let’s dive in!
Changed from Termion to Rustyline
Given the difficulties with Termion and since I don’t need to create a complex terminal layout, I decided to switch to Rustyline. This crate allows you to do simple things like letting the user edit the input, having a customizable prompt, command history, and other technical features.
This change was not only beneficial in terms of UI and features but also in performance and code readability. Instead of configuring the terminal layout from scratch, which is great for complex layouts, Rustyline provides a basic layout and events you can listen to before receiving the input, something the standard library lacks.
Here is the read_input
function implemented with Rustyline. It’s much simpler and more readable than with Termion:
pub fn read_input(rl: &mut Editor<(), rustyline::history::FileHistory>) -> Option<String> {
// Read user input from the console using the rustyline library
match rl.readline(get_prompt().as_str()) {
// If the input was successfully read, add it to the history and return it
Ok(input) => {
if rl.add_history_entry(input.as_str()).is_err() {
println!("Failed to add input to history");
}
Some(input)
}
Err(rustyline::error::ReadlineError::Interrupted) => {
println!("Interrupted process: {:?}", rustyline::error::ReadlineError::Interrupted);
None
}
// If there was an error reading the input, print the error and return an empty String
Err(err) => {
println!("Error: {:?}", err);
None
}
}
}
History Command
Just like Bash has the history
command, which allows you to view and manipulate the command history, I wanted to implement a similar feature in our shell. This enables users to customize their command history.
Adding this feature was straightforward since Rustyline already has built-in support for command history. The implementation of the history command was also surprisingly simple. It doesn’t do as much as the Bash version, but it is sufficient. The history file is stored in the home directory with the name .oxsh_history
,” similar to Bash.
Init Script
I also added the possibility to read an initial script when starting the shell. This script has to be situated in the home directory of the user using the shell, but in the future, I might implement global init scripts.
These scripts are written in Bash, so executing and displaying the output to the shell is fairly easy. I know it’s kind of ironic, but I didn’t want to spend time making my own scripting language as it would consume a lot of time and wouldn’t be very beneficial, at least for now.
Installation Script
Instead of having to download the whole project and compile it yourself, I made a brief installation script that does this for you. Additionally, it creates the init script with a default message and manages all the executable permissions so that you can directly execute the oxsh
program and be ready to go.
What Now?
I think this will be the last post of the shell series, as it has achieved its goal: helping me learn Rust and understand it better. I might make a new post in the future expanding the project, but I’m not sure about it yet.
I also want to focus on other things, like OpenGL
or SDL2
. So, I think I will start a new project to work on that, as graphic displays are something I wanted to learn since before starting this project. The only problem was that I wasn’t familiar with the language itself, and I honestly didn’t want to do it in C++ or C because of memory insecurities. But who knows, maybe I will have to, especially with OpenGL
or other 3D APIs.
So, I’ll see you in the next project!