Skip to content

What42Pizza/rust-smart-read

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

26 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Smart Read

Crates.io Crates.io license

Complex but easy ways to read user input

Anything that implements TryRead can be used with the read and prompt macros, and features are added by creating new implementations of this trait. For a list of default implementations, just go to the main docs page.


Qualities

  • Extremely Customizable, basically anything can be implemented
  • Extremely Simple, for both using and extending
  • Extremely Ergonomic, everything is as effortless as possible

Basic Usage

// read a line of text
let _ = read!();

// prompt a line of text
let _ = prompt!("Enter a string: ");

// read specific types
let _ = read!(UsizeInput);
let _ = read!(BoolInput);
let _ = read!(NonWhitespaceInput);
let _ = read!(I32Input);

// read a number within a range
let _ = read!(0. ..= 100.);


// read a bool
let _ = prompt!("Confirm input: "; YesNoInput);

// set a default value
let _ = prompt!("Confirm input: "; [true] YesNoInput);


// choose from a list of options
let _ = read!(&["red", "green", "blue"]);

// some input type have special syntax
let _ = read!(= "red", "green", "blue");


// one-time custom logic
let _ = prompt!("Enter an even int: "; TransformValidate (|x: String| -> Result<isize, String> { // explicit types here are optional, only added for demonstration
	let Ok(x) = x.parse::<isize>() else {return Err(String::from("Could not parse input"));};
	if x % 2 != 0 {return Err(String::from("Input is not even"));}
	Ok(x)
}));


// combine any features
let _ = prompt!("Enter an int: "; [1] = 1, 2, 3, 4, 5);

Example stdout (from the read_lines example)

==== `read!(0. ..= 100.)` ====
Enter a number within the range [0.0, 100.0]: 100.0001

Invalid input, not within bounds
Enter a number within the range [0.0, 100.0]: aa 

Could not parse input (error: invalid float literal)
Enter a number within the range [0.0, 100.0]: 1.
You entered: "1"

Extend Existing Functionality

use smart_read::prelude::*;

#[derive(Clone, PartialEq)]
pub struct Car {
	pub name: String,
	pub color: String,
}

// choose from a list of cars
fn main() {
	let input = read!(= new_car("Red", "Toyota"), new_car("Silver", "Ram"));
	println!("You chose: {input}");
}

pub fn new_car(color: impl Into<String>, name: impl Into<String>) -> Car {
	Car {name: name.into(), color: color.into()}
}

impl std::fmt::Display for Car {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		write!(f, "{} {}", self.color, self.name)
	}
}

Add New Functionality

use smart_read::*;

struct PasswordInput {
	pub min_len: usize,
	pub min_digits: usize,
}

// take in a password input
fn main() {
	let input = read!(PasswordInput {min_len: 10, min_digits: 1});
	println!("You entered: \"{input}\"");
}

impl TryRead for PasswordInput {
	type Output = String;
	fn try_read_line(&self, read_data: TryReadArgs<Self::Output>) -> smart_read::BoxResult<Self::Output> {
		if read_data.default.is_some() {return DefaultNotAllowedError::new_box_result();}
		let prompt = read_data.prompt.unwrap_or_else(
			|| format!("Enter a password (must have {}+ characters and have {}+ digits): ", self.min_len, self.min_digits)
		);
		loop {
			
			print!("{prompt}");
			let password = read_stdin()?;
			
			if password.len() < 10 {
				println!("Password must have at least 10 characters");
				continue;
			}
			if password.chars().filter(|c| c.is_digit(10)).count() < 1 {
				println!("Password must have at least 1 digit");
				continue;
			}
			
			return Ok(password)
			
		}
	}
}

About

This is my first crate, an easy way to read stdin

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages