Building a Decision-Making CLI Tool in Rust: “should-i”



This content originally appeared on DEV Community and was authored by justhiro

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

should-i in action

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! 🎉

Links


This content originally appeared on DEV Community and was authored by justhiro