wheel-picker 1.0.3

Simple cli tool to make and randomly pick from a wheel.
use std::io::{self, Write};
use std::thread;
use std::time::Duration;

use lexopt::{Arg, Parser};

use crate::config::Config;

mod config;

fn main() {
	config::create_config();
	let mut config = Config::get();

	let mut parser = Parser::from_env();
	let cmd = match parser.next() {
		| Ok(Some(Arg::Value(v))) => v.to_string_lossy().into_owned(),
		| _ => {
			eprintln!(
				"wheel-picker v{}\n\nusage: wheel <add|remove|list|clear|pick> [...]",
				env!("CARGO_PKG_VERSION")
			);
			return;
		},
	};

	match cmd.as_str() {
		| "add" => {
			let name = match parser.next() {
				| Ok(Some(Arg::Value(v))) => v.to_string_lossy().into_owned(),
				| _ => {
					eprintln!("usage: wheel add <NAME>");
					return;
				},
			};

			config.current_pool.push(name.clone());
			config.update();
			println!("added entry {name} to wheel\n\nrun `wheel-picker list` to see all entries");
		},

		| "remove" => {
			let name = match parser.next() {
				| Ok(Some(Arg::Value(v))) => v.to_string_lossy().into_owned(),
				| _ => {
					eprintln!("usage: wheel remove <NAME>");
					return;
				},
			};

			remove_user(config.clone(), &name);
		},

		| "list" => {
			if config.current_pool.is_empty() {
				println!("wheel has no entries\n\ntry adding some with `wheel-picker add <entry>`");
			} else {
				println!("current wheel pool:\n{}", config.current_pool.join("\n"));
			}
		},

		| "clear" => {
			print!("Are you sure? (y/N): ");
			io::stdout().flush().unwrap();

			let mut input = String::new();
			io::stdin().read_line(&mut input).unwrap();

			let input = input.trim().to_lowercase();
			if input == "y" || input == "yes" {
				config.current_pool.clear();
				config.update();
				println!("cleared wheel");
			} else {
				println!("cancelled operation");
			}
		},

		| "pick" => {
			let mut instant = false;
			let mut remove_after_pick = false;
			let mut amount: usize = 1;

			while let Some(Ok(arg)) = parser.next().transpose() {
				match arg {
					| Arg::Long("instant") | Arg::Short('i') => instant = true,
					| Arg::Long("remove") | Arg::Short('r') => remove_after_pick = true,
					| Arg::Value(v) => amount = v.to_string_lossy().parse().unwrap_or(1),
					| _ => {},
				}
			}

			if config.current_pool.is_empty() {
				println!("wheel has no entries\n\ntry adding some with `wheel-picker add <entry>`");
				return;
			}

			if instant {
				let mut picked: Vec<&str> = Vec::new();
				for _ in 0..amount {
					let new_pick = fastrand::choice(&config.current_pool).unwrap();

					picked.push(&new_pick);
					if remove_after_pick {
						remove_user(config.clone(), &new_pick)
					}
				}
				println!("picked entries: {}", picked.join(", "));
			} else {
				let mut picked = Vec::new();
				for i in 0..amount {
					println!("\nSpin {}:", i + 1);

					let spin_time = fastrand::usize(1..3);
					let start = std::time::Instant::now();
					let mut current = "";

					while start.elapsed().as_secs_f32() < spin_time as f32 {
						current = fastrand::choice(&config.current_pool).unwrap();
						print!("\r\x1B[2KSpinning... [{}]", current);
						io::stdout().flush().unwrap();
						thread::sleep(Duration::from_millis(fastrand::u64(50..150)));
					}

					picked.push(current);
					if remove_after_pick {
						remove_user(config.clone(), &current.to_string())
					}

					println!("\r\x1B[2Kpicked: {}", current);
				}

				println!("\npicked entries: {}", picked.join(", "));
			}
		},

		| _ => eprintln!("unknown command {cmd}"),
	}
}

fn remove_user(
	mut config: Config,
	name: &String,
) {
	if let Some(pos) = config.current_pool.iter().position(|e| e == name) {
		config.current_pool.remove(pos);
		config.update();
		println!("removed entry {name} from wheel\n\nrun `wheel-picker list` to see all entries");
	} else {
		println!("entry {name} not found in wheel");
	}
}