Introduction
“What should I have for lunch?” “Should I try this new library?” From daily small decisions to technical choices, we face countless decisions every day. Why not let the universe decide for you?
I built a CLI tool called “should-i” that helps you make decisions by consulting the yesno.wtf API. In this article, I’ll share the implementation details and technical insights.
What is should-i?
should-i is a simple CLI tool that answers your questions with YES/NO/MAYBE. It also returns a GIF image URL, making the experience more engaging and visual.
Basic Usage
Browser GIF Display with –open Option
With the --open option, the tool automatically opens the response GIF in your browser:
$ should-i "take a coffee break" --open
🎲 A...
Introduction
“What should I have for lunch?” “Should I try this new library?” From daily small decisions to technical choices, we face countless decisions every day. Why not let the universe decide for you?
I built a CLI tool called “should-i” that helps you make decisions by consulting the yesno.wtf API. In this article, I’ll share the implementation details and technical insights.
What is should-i?
should-i is a simple CLI tool that answers your questions with YES/NO/MAYBE. It also returns a GIF image URL, making the experience more engaging and visual.
Basic Usage
Browser GIF Display with –open Option
With the --open option, the tool automatically opens the response GIF in your browser:
$ should-i "take a coffee break" --open
🎲 Asking the universe...
✅ YES! Do it! 🎉
🖼️ https://yesno.wtf/assets/yes/3.gif
# Browser opens with the GIF automatically
Tech Stack
This project uses the following Rust crates:
- reqwest: HTTP client
- tokio: Async runtime
- serde/serde_json: JSON serialization/deserialization
- clap: CLI argument parser
- anyhow: Error handling
- webbrowser: Browser opening functionality
Implementation Highlights
1. API Communication
The yesno.wtf API is a simple RESTful API with responses structured like this:
{
"answer": "yes",
"forced": false,
"image": "https://yesno.wtf/assets/yes/2.gif"
}
We map this to a Rust struct:
#[derive(Deserialize)]
struct YesNoResponse {
answer: String,
image: String,
}
Making async HTTP requests with reqwest is simple and type-safe:
let response = client
.get("https://yesno.wtf/api")
.send()
.await?
.json::()
.await?;
2. CLI Interface Design
Using clap makes implementing an intuitive CLI straightforward. The derive macro approach is very Rust-like, automatically generating a parser from struct definitions:
#[derive(Parser)]
#[command(name = "should-i")]
#[command(about = "A CLI tool to help you make decisions")]
struct Cli {
/// The question you want to ask
question: String,
/// Open the GIF image in your browser
#[arg(short, long)]
open: bool,
}
3. Enhancing User Experience
Rather than just displaying results, I used emojis to make the output visually clear and engaging:
match response.answer.as_str() {
"yes" => println!("✅ YES! Do it! 🎉"),
"no" => println!("❌ NO! Don't do it! 🚫"),
"maybe" => println!("🤔 MAYBE... You decide! 🎲"),
_ => println!("🤷 Unknown response"),
}
The --open option automatically opens the GIF in your browser:
if cli.open {
if let Err(e) = webbrowser::open(&response.image) {
eprintln!("⚠️ Failed to open browser: {}", e);
}
}
This feature lets you visually enjoy the universe’s message, which is one of the tool’s charms.
Error Handling
Leveraging Rust’s powerful type system, I implemented proper error handling using the anyhow crate:
async fn fetch_decision() -> anyhow::Result {
let client = reqwest::Client::new();
let response = client
.get("https://yesno.wtf/api")
.send()
.await
.context("Failed to connect to yesno.wtf API")?
.json::()
.await
.context("Failed to parse API response")?;
Ok(response)
}
The context() method provides specific context when errors occur.
Project Structure
should-i/
├── Cargo.toml # Dependencies and metadata
├── src/
│ └── main.rs # Main implementation
├── LICENSE-MIT
├── LICENSE-APACHE
└── README.md
As a simple tool, everything is contained in a single file. For larger projects, consider splitting into modules.
Installation
Using Cargo
cargo install should-i
Using Homebrew
brew tap Justhiro55/tap
brew install should-i
From Source
git clone https://github.com/Justhiro55/should-i
cd should-i
cargo install --path .
Conclusion
should-i is simple, but it contains fundamental elements of Rust CLI development:
- Async HTTP communication
- JSON parsing
- CLI argument handling
- Error handling
- Cross-platform support
The playful concept of “letting the universe decide” made implementing practical Rust techniques enjoyable.
When in doubt, just ask should-i!
$ should-i "write more Rust code"
🎲 Asking the universe...
✅ YES! Do it! 🎉