/* lib.rs contains all the logic of the program */ use std::{error::Error, fmt::Display, str::FromStr, io::{self, Write}}; /// handles setup for the game pub struct Config { diamond_size: isize, } impl Config { /// creates and returns a new Config from user input pub fn new() -> Result> { //DATA let mut config: Config = Config { diamond_size: 0 }; //get data from user input //get num players println!("FOR A PRETTY DIAMOND PATTERN,"); //input looop config.diamond_size = loop { match get_number_from_input("TYPE IN AN ODD NUMBER BETWEEN 5 AND 31 ", 5, 31) { Ok(num) => { //ensure num is odd if num%2 == 0 {continue;} else {break num;} }, Err(e) => { eprintln!("{}",e); continue; }, } }; //return new config return Ok(config); } } /// run the program pub fn run(config: &Config) -> Result<(), Box> { //DATA let line_width: isize = 60; let padding: char = 'C'; let pixel_width: isize = 1; let filling: char = '!'; let border: char = '#'; let width_of_full_diamonds_in_line = (line_width/config.diamond_size) * config.diamond_size; //print top border println!("{}", n_chars(width_of_full_diamonds_in_line+2, border)); //print out diamonds for row in 0..width_of_full_diamonds_in_line { print_diamond_line(config.diamond_size, row, line_width, pixel_width, padding, filling, border); } //print bottom border println!("{}", n_chars(width_of_full_diamonds_in_line+2, border)); //return to main Ok(()) } /// prints the next line of diamonds fn print_diamond_line(diamond_width: isize,row: isize, line_width:isize, pixel_width:isize, padding:char, filling:char, border:char) { //DATA let diamonds_per_row = (line_width/pixel_width) / diamond_width; //let row = row % (diamonds_per_row - 1); let padding_amount; //total amount of padding before and after the filling of each diamond in this row let filling_amount; //amount of "diamond" in each diamond in this row //calculate padding padding_amount = (2 * ( (row%(diamond_width-1)) - (diamond_width/2))).abs(); //calculate filling filling_amount = -padding_amount + diamond_width; //print border before every row print!("{}", border); //for every diamond in this row: for _diamond in 0..diamonds_per_row { //print leading padding print!("{}", n_chars( pixel_width * padding_amount/2, padding ) ); //print filling print!("{}", n_chars( pixel_width * filling_amount , filling ) ); //print trailing padding print!("{}", n_chars( pixel_width * padding_amount/2, padding ) ); } //print border after every row print!("{}", border); //new line println!(""); } /// returns n of the passed character, put into a string fn n_chars(n:isize, character: char) -> String { let mut output = String::new(); for _i in 0..n { output.push(character); } output } /// gets a string from user input fn get_string_from_user_input(prompt: &str) -> Result> { //DATA let mut raw_input = String::new(); //print prompt print!("{}", prompt); //make sure it's printed before getting input io::stdout().flush().unwrap(); //read user input from standard input, and store it to raw_input, then return it or an error as needed raw_input.clear(); //clear input match io::stdin().read_line(&mut raw_input) { Ok(_num_bytes_read) => return Ok(String::from(raw_input.trim())), Err(err) => return Err(format!("ERROR: CANNOT READ INPUT!: {}", err).into()), } } /// generic function to get a number from the passed string (user input) /// pass a min lower than the max to have minimun and maximun bounds /// pass a min higher than the max to only have a minumum bound /// pass a min equal to the max to only have a maximun bound /// /// Errors: /// no number on user input fn get_number_from_input(prompt: &str, min:T, max:T) -> Result> { //DATA let raw_input: String; let processed_input: String; //input looop raw_input = loop { match get_string_from_user_input(prompt) { Ok(input) => break input, Err(e) => { eprintln!("{}",e); continue; }, } }; //filter out num-numeric characters from user input processed_input = raw_input.chars().filter(|c| c.is_numeric()).collect(); //from input, try to read a number match processed_input.trim().parse() { Ok(i) => { //what bounds must the input fall into if min < max { //have a min and max bound: [min,max] if i >= min && i <= max {//is input valid, within bounds return Ok(i); //exit the loop with the value i, returning it } else { //print error message specific to this case return Err(format!("ONLY BETWEEN {} AND {}, PLEASE!", min, max).into()); } } else if min > max { //only a min bound: [min, infinity) if i >= min { return Ok(i); } else { return Err(format!("NO LESS THAN {}, PLEASE!", min).into()); } } else { //only a max bound: (-infinity, max] if i <= max { return Ok(i); } else { return Err(format!("NO MORE THAN {}, PLEASE!", max).into()); } } }, Err(_e) => return Err(format!("Error: couldn't find a valid number in {}",raw_input).into()), } }