Skip to content

richarddalves/stdin-helper

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 

Repository files navigation

stdin-helper

Status Version License

A simple, intuitive Rust library for reading typed user input from stdin. Stop fighting with read_line() and parsing - just ask for what you need.

What is this?

stdin-helper provides straightforward functions for getting typed input from the user. If you've ever been frustrated writing the same read_line() boilerplate over and over, this library is for you.

Every function accepts a repeat flag: when set to true, it automatically retries on invalid input. Ask for an integer and the user types "hello"? It will ask again until it gets a valid number. When set to false, it returns a Result immediately, giving you full control over error handling.

Quick Start

Add to your Cargo.toml:

[dependencies]
stdin-helper = "2.0.1"

Or use cargo:

cargo add stdin-helper

Basic Usage

All functions return InputResult<T>, which is an alias for Result<T, InputError<...>>. Use repeat: true to loop until valid input is received, or repeat: false to get a Result back immediately.

use stdin_helper::{get_string, get_i32, get_f64, get_bool};

fn main() {
    // repeat: true — keeps asking until the user enters something valid
    let name = get_string("What's your name? ", true).unwrap();
    let age = get_i32("How old are you? ", true).unwrap();
    let height = get_f64("Height in meters: ", true).unwrap();
    let verified = get_bool("Are you human? ", true, &["yes", "y", "true"], &["no", "n", "false"]).unwrap();

    println!("{} is {} years old and {:.2}m tall", name, age, height);
}

When using repeat: false, you can propagate or handle errors idiomatically:

use stdin_helper::get_i32;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let age = get_i32("How old are you? ", false)?;
    println!("You are {age} years old.");
    Ok(())
}

Error Handling

stdin-helper distinguishes between two kinds of failures via InputError<E>:

  • InputError::Io(io::Error) — something went wrong reading from stdin (e.g. stdin was closed).
  • InputError::Parse(E) — the user's input couldn't be converted to the requested type.

This distinction matters because I/O errors are generally unrecoverable, while parse errors are expected and safe to retry. When repeat: true is used, parse errors are silently retried; I/O errors are still returned immediately regardless of the flag.

use stdin_helper::{get_i32, InputError};

match get_i32("Enter a number: ", false) {
    Ok(n) => println!("Got: {n}"),
    Err(InputError::Io(e)) => eprintln!("I/O error: {e}"),
    Err(InputError::Parse(e)) => eprintln!("Invalid input: {e}"),
}

Available Functions

Text Input

Function Returns Description
get_string(prompt, repeat) InputResult<String> Gets text input (any text is valid)
get_char(prompt, repeat) InputResult<char> Gets a single character

Integer Types

Function Returns Range
get_i8(prompt, repeat) InputResult<i8> -128 to 127
get_i16(prompt, repeat) InputResult<i16> -32,768 to 32,767
get_i32(prompt, repeat) InputResult<i32> -2,147,483,648 to 2,147,483,647
get_i64(prompt, repeat) InputResult<i64> -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
get_i128(prompt, repeat) InputResult<i128> -(2^127) to 2^127 - 1
get_isize(prompt, repeat) InputResult<isize> Platform-dependent (32 or 64 bits)

Unsigned Integer Types

Function Returns Range
get_u8(prompt, repeat) InputResult<u8> 0 to 255
get_u16(prompt, repeat) InputResult<u16> 0 to 65,535
get_u32(prompt, repeat) InputResult<u32> 0 to 4,294,967,295
get_u64(prompt, repeat) InputResult<u64> 0 to 18,446,744,073,709,551,615
get_u128(prompt, repeat) InputResult<u128> 0 to 2^128 - 1
get_usize(prompt, repeat) InputResult<usize> Platform-dependent (32 or 64 bits)

Floating Point Types

Function Returns Description
get_f32(prompt, repeat) InputResult<f32> 32-bit floating point
get_f64(prompt, repeat) InputResult<f64> 64-bit floating point (recommended)

Boolean Input

Boolean input requires you to explicitly define which strings count as true and which count as false. Comparison is case-insensitive after .trim().

// Signature
pub fn get_bool(
    msg: &str,
    repeat: bool,
    true_values: &[&str],
    false_values: &[&str],
) -> Result<bool, InputError<BoolParseError>>
let confirmed = get_bool(
    "Confirm? ",
    true,
    &["yes", "y", "sim", "s", "true", "1"],
    &["no", "n", "nao", "false", "0"],
).unwrap();

Generic Input

If you need a type not listed above, use the core generic function directly. Any type that implements FromStr works:

use stdin_helper::get_input;

let value: u64 = get_input("Enter a value: ", true).unwrap();

For numeric types specifically, get_number<T> is also available with a Numeric trait bound that prevents accidental misuse with non-numeric types:

use stdin_helper::get_number;

let value: f32 = get_number("Enter a value: ", true).unwrap();

Examples

Simple Calculator

use stdin_helper::{get_i32, get_string};

fn main() {
    let x = get_i32("First number: ", true).unwrap();
    let op = get_string("Operator (+, -, *, /): ", true).unwrap();
    let y = get_i32("Second number: ", true).unwrap();

    let result = match op.as_str() {
        "+" => x + y,
        "-" => x - y,
        "*" => x * y,
        "/" => x / y,
        _ => {
            println!("Invalid operator");
            return;
        }
    };

    println!("{} {} {} = {}", x, op, y, result);
}

Temperature Converter

use stdin_helper::{get_f64, get_string};

fn main() {
    let temp = get_f64("Enter temperature: ", true).unwrap();
    let unit = get_string("Unit (C/F): ", true).unwrap().to_uppercase();

    match unit.as_str() {
        "C" => {
            let fahrenheit = (temp * 9.0 / 5.0) + 32.0;
            println!("{}°C = {:.1}°F", temp, fahrenheit);
        }
        "F" => {
            let celsius = (temp - 32.0) * 5.0 / 9.0;
            println!("{}°F = {:.1}°C", temp, celsius);
        }
        _ => println!("Invalid unit. Use C or F."),
    }
}

Grade Calculator

use stdin_helper::{get_f32, get_i32};

fn main() {
    let num_grades = get_i32("How many grades? ", true).unwrap();
    let mut sum = 0.0;

    for i in 1..=num_grades {
        let grade = get_f32(&format!("Grade {}: ", i), true).unwrap();
        sum += grade;
    }

    let average = sum / num_grades as f32;
    println!("Average: {:.2}", average);
}

Propagating Errors

use stdin_helper::get_i32;

fn read_positive() -> Result<i32, Box<dyn std::error::Error>> {
    // repeat: false gives us the Result immediately so we can propagate
    let n = get_i32("Enter a number: ", false)?;
    Ok(n)
}

How It Works

Each function displays your prompt and waits for a line of input. What happens next depends on the repeat flag:

  • repeat: true — if the input can't be parsed into the requested type, the prompt is displayed again. This continues until valid input is received or an I/O error occurs.
  • repeat: false — tries once and returns a Result. A parse failure becomes Err(InputError::Parse(...)), which you can handle or propagate with ?.

I/O errors (InputError::Io) always surface immediately regardless of the repeat flag, since they signal a problem with stdin itself rather than with the user's input.

Future Plans

In upcoming versions, stdin-helper will support a builder pattern API for more advanced input validation scenarios:

// THIS DOES NOT WORK YET - Coming in a future version!
use stdin_helper::InputBuilder;

let user_rating: u32 = InputBuilder::new("Please input a rate: ")
    .range(1..=10, "Number must be between 1 and 10")
    .on_error("That doesn't look like a number")
    .get()?;

This will allow you to:

  • Define custom validation rules
  • Provide specific error messages for different validation failures
  • Set retry limits
  • Add custom validators with closures

If you'd like to contribute to making this happen, check out the repository!

Building and Testing

# Build the library
cargo build

# Run tests
cargo test

# Generate documentation
cargo doc --open

Why Use This?

For beginners: Eliminates boilerplate so you can focus on learning Rust concepts rather than input handling mechanics. Use repeat: true and .unwrap() to get started fast.

For prototyping: Quickly test ideas without writing parsing code every time.

For simple CLIs: Perfect for straightforward command-line tools where complex error handling isn't needed.

For teaching: Great for educational projects and coding exercises where input handling shouldn't be the focus.

When Not to Use This

This library is designed for simplicity, not for complex input scenarios. Consider alternatives if you need:

  • Non-blocking input handling
  • Input with timeout limits
  • Production-grade validation with custom retry limits or closures (coming in a future version)

For those cases, using std::io directly or other crates might be better choices.

License

Licensed under either of:

at your option.

Contributing

Contributions are welcome! Whether it's:

  • Bug reports and feature requests
  • Documentation improvements
  • Code contributions
  • Examples and use cases

Feel free to open an issue or submit a pull request.

Author

Created by Richard Dias Alves

Acknowledgments

Inspired by the frustration of writing read_line() boilerplate repeatedly. Built to make Rust's stdin interaction as simple as it should be.


Repository: https://github.com/richarddalves/stdin-helper
Crate: https://crates.io/crates/stdin-helper